mangotools 1.0.0__tar.gz
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.
- mangotools-1.0.0/LICENSE +21 -0
- mangotools-1.0.0/MANIFEST.in +3 -0
- mangotools-1.0.0/PKG-INFO +34 -0
- mangotools-1.0.0/README.md +13 -0
- mangotools-1.0.0/mangotools/__init__.py +6 -0
- mangotools-1.0.0/mangotools/assertion/__init__.py +26 -0
- mangotools-1.0.0/mangotools/assertion/_custom_assertion.py +27 -0
- mangotools-1.0.0/mangotools/assertion/_public_assertion.py +291 -0
- mangotools-1.0.0/mangotools/assertion/_sql_assertion.py +29 -0
- mangotools-1.0.0/mangotools/data_processor/__init__.py +123 -0
- mangotools-1.0.0/mangotools/data_processor/_cache_tool.py +38 -0
- mangotools-1.0.0/mangotools/data_processor/_coding_tool.py +21 -0
- mangotools-1.0.0/mangotools/data_processor/_encryption_tool.py +44 -0
- mangotools-1.0.0/mangotools/data_processor/_json_tool.py +60 -0
- mangotools-1.0.0/mangotools/data_processor/_random_character_info_data.py +69 -0
- mangotools-1.0.0/mangotools/data_processor/_random_number_data.py +79 -0
- mangotools-1.0.0/mangotools/data_processor/_random_string_data.py +65 -0
- mangotools-1.0.0/mangotools/data_processor/_random_time_data.py +219 -0
- mangotools-1.0.0/mangotools/data_processor/_sql_cache.py +133 -0
- mangotools-1.0.0/mangotools/database/__init__.py +16 -0
- mangotools-1.0.0/mangotools/database/_mysql_connect.py +64 -0
- mangotools-1.0.0/mangotools/database/_sqlite_connect.py +41 -0
- mangotools-1.0.0/mangotools/decorator/__init__.py +24 -0
- mangotools-1.0.0/mangotools/decorator/convert_args.py +29 -0
- mangotools-1.0.0/mangotools/decorator/inject_to_class.py +23 -0
- mangotools-1.0.0/mangotools/decorator/method_callback.py +65 -0
- mangotools-1.0.0/mangotools/decorator/retry.py +45 -0
- mangotools-1.0.0/mangotools/decorator/singleton.py +21 -0
- mangotools-1.0.0/mangotools/enums/__init__.py +17 -0
- mangotools-1.0.0/mangotools/enums/_base_enum.py +38 -0
- mangotools-1.0.0/mangotools/enums/_enums.py +44 -0
- mangotools-1.0.0/mangotools/exceptions/__init__.py +13 -0
- mangotools-1.0.0/mangotools/exceptions/_error_msg.py +15 -0
- mangotools-1.0.0/mangotools/exceptions/_exceptions.py +29 -0
- mangotools-1.0.0/mangotools/log_collector/__init__.py +66 -0
- mangotools-1.0.0/mangotools/log_collector/_log_control.py +59 -0
- mangotools-1.0.0/mangotools/mangos/__init__.py +74 -0
- mangotools-1.0.0/mangotools/mangos/pyarmor_runtime_linux/__init__.py +5 -0
- mangotools-1.0.0/mangotools/mangos/pyarmor_runtime_linux/mango.py +3 -0
- mangotools-1.0.0/mangotools/mangos/pyarmor_runtime_linux/pyarmor_runtime_000000/__init__.py +2 -0
- mangotools-1.0.0/mangotools/mangos/pyarmor_runtime_linux/pyarmor_runtime_000000/pyarmor_runtime.so +0 -0
- mangotools-1.0.0/mangotools/mangos/pyarmor_runtime_windows/__init__.py +5 -0
- mangotools-1.0.0/mangotools/mangos/pyarmor_runtime_windows/mango.py +3 -0
- mangotools-1.0.0/mangotools/mangos/pyarmor_runtime_windows/pyarmor_runtime_000000/__init__.py +2 -0
- mangotools-1.0.0/mangotools/mangos/pyarmor_runtime_windows/pyarmor_runtime_000000/pyarmor_runtime.pyd +0 -0
- mangotools-1.0.0/mangotools/method.py +96 -0
- mangotools-1.0.0/mangotools/models/__init__.py +21 -0
- mangotools-1.0.0/mangotools/models/_models.py +61 -0
- mangotools-1.0.0/mangotools/notice/__init__.py +17 -0
- mangotools-1.0.0/mangotools/notice/_mail_send.py +70 -0
- mangotools-1.0.0/mangotools/notice/_wechat_send.py +98 -0
- mangotools-1.0.0/mangotools.egg-info/PKG-INFO +34 -0
- mangotools-1.0.0/mangotools.egg-info/SOURCES.txt +60 -0
- mangotools-1.0.0/mangotools.egg-info/dependency_links.txt +1 -0
- mangotools-1.0.0/mangotools.egg-info/requires.txt +11 -0
- mangotools-1.0.0/mangotools.egg-info/top_level.txt +2 -0
- mangotools-1.0.0/setup.cfg +4 -0
- mangotools-1.0.0/setup.py +51 -0
- mangotools-1.0.0/tests/__init__.py +5 -0
- mangotools-1.0.0/tests/cache_test.py +50 -0
- mangotools-1.0.0/tests/test.py +72 -0
- mangotools-1.0.0/tests/test_sql_cache.py +73 -0
mangotools-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 maopeng
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: mangotools
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: 测试工具
|
|
5
|
+
Home-page: https://gitee.com/mao-peng/testkit
|
|
6
|
+
Author: 毛鹏
|
|
7
|
+
Author-email: 729164035@qq.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: aiomysql==0.2.0
|
|
11
|
+
Requires-Dist: PyMySQL==1.1.1
|
|
12
|
+
Requires-Dist: jsonpath==0.82.2
|
|
13
|
+
Requires-Dist: cachetools==5.3.1
|
|
14
|
+
Requires-Dist: Faker==24.1.0
|
|
15
|
+
Requires-Dist: diskcache==5.6.3
|
|
16
|
+
Requires-Dist: pydantic==2.9.2
|
|
17
|
+
Requires-Dist: colorlog==6.7.0
|
|
18
|
+
Requires-Dist: pyarmor==9.0.6
|
|
19
|
+
Requires-Dist: assertpy==1.1
|
|
20
|
+
Requires-Dist: deepdiff==8.0.1
|
|
21
|
+
|
|
22
|
+
# testkit
|
|
23
|
+
|
|
24
|
+
#### 介绍
|
|
25
|
+
|
|
26
|
+
测试工具
|
|
27
|
+
|
|
28
|
+
#### 安装教程
|
|
29
|
+
|
|
30
|
+
1. pip install mangokit
|
|
31
|
+
|
|
32
|
+
#### 使用说明
|
|
33
|
+
|
|
34
|
+
1.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Project: 芒果测试平台# @Description:
|
|
3
|
+
# @Time : 2023/4/6 13:36
|
|
4
|
+
# @Author : 毛鹏
|
|
5
|
+
|
|
6
|
+
from ..assertion._custom_assertion import CustomAssertion
|
|
7
|
+
from ..assertion._public_assertion import WhatIsItAssertion, ContainAssertion, MatchingAssertion, \
|
|
8
|
+
WhatIsEqualToAssertion, PublicAssertion
|
|
9
|
+
from ..assertion._sql_assertion import SqlAssertion
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
python_version = sys.version_info
|
|
13
|
+
if f"{python_version.major}.{python_version.minor}" != "3.10":
|
|
14
|
+
raise Exception("必须使用>Python3.10.4")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Assertion(WhatIsItAssertion, ContainAssertion, MatchingAssertion, WhatIsEqualToAssertion):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
'Assertion',
|
|
23
|
+
'CustomAssertion',
|
|
24
|
+
'SqlAssertion',
|
|
25
|
+
'PublicAssertion',
|
|
26
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Project: 芒果测试平台
|
|
3
|
+
# @Description:
|
|
4
|
+
# @Time : 2024-01-05 18:05
|
|
5
|
+
# @Author : 毛鹏
|
|
6
|
+
from ..decorator import sync_method_callback
|
|
7
|
+
from ..exceptions import ERROR_MSG_0061, MangoKitError
|
|
8
|
+
from ..models import MethodModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CustomAssertion:
|
|
12
|
+
"""自定义断言"""
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
@sync_method_callback('ass', '自定义断言', 7, [
|
|
16
|
+
MethodModel(f='func_str', p='请输入一个函数,在函数里面自己断言', d=True),
|
|
17
|
+
MethodModel(f='func_name', p='请输入这个函数的名称', d=True), ])
|
|
18
|
+
def ass_func(func_str, func_name='func'):
|
|
19
|
+
"""输入断言代码"""
|
|
20
|
+
try:
|
|
21
|
+
global_namespace = {}
|
|
22
|
+
exec(func_str, global_namespace)
|
|
23
|
+
return global_namespace[func_name]
|
|
24
|
+
except (KeyError, SyntaxError, TypeError):
|
|
25
|
+
import traceback
|
|
26
|
+
traceback.print_exc()
|
|
27
|
+
raise MangoKitError(*ERROR_MSG_0061)
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Project: 芒果测试平台# @Description:
|
|
3
|
+
# @Time : 2023-09-09 23:17
|
|
4
|
+
# @Author : 毛鹏
|
|
5
|
+
from assertpy import assert_that
|
|
6
|
+
from deepdiff import DeepDiff
|
|
7
|
+
|
|
8
|
+
from mangokit.decorator import sync_method_callback
|
|
9
|
+
from mangokit.models import MethodModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class WhatIsItAssertion:
|
|
13
|
+
"""是什么"""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
@sync_method_callback('ass', '是什么', 0, [
|
|
17
|
+
MethodModel(f='actual', d=True)])
|
|
18
|
+
def p_is_not_none(actual):
|
|
19
|
+
"""不是null"""
|
|
20
|
+
try:
|
|
21
|
+
assert_that(actual).is_not_none()
|
|
22
|
+
except AssertionError as e:
|
|
23
|
+
raise AssertionError(f'实际={actual}, 预期=不是null') from e
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
@sync_method_callback('ass', '是什么', 1, [
|
|
27
|
+
MethodModel(f='actual', d=True)])
|
|
28
|
+
def p_is_none(actual):
|
|
29
|
+
"""是null"""
|
|
30
|
+
try:
|
|
31
|
+
assert_that(actual).is_none()
|
|
32
|
+
except AssertionError as e:
|
|
33
|
+
raise AssertionError(f'实际={actual}, 预期=是null') from e
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
@sync_method_callback('ass', '是什么', 2, [
|
|
37
|
+
MethodModel(f='actual', d=True)])
|
|
38
|
+
def p_is_empty(actual):
|
|
39
|
+
"""是空字符串"""
|
|
40
|
+
try:
|
|
41
|
+
assert_that(actual).is_empty()
|
|
42
|
+
except AssertionError as e:
|
|
43
|
+
raise AssertionError(f'实际={actual}, 预期=是空字符串') from e
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
@sync_method_callback('ass', '是什么', 3, [
|
|
47
|
+
MethodModel(f='actual', d=True)])
|
|
48
|
+
def p_is_not_empty(actual):
|
|
49
|
+
"""不是空符串"""
|
|
50
|
+
try:
|
|
51
|
+
assert_that(actual).is_not_empty()
|
|
52
|
+
except AssertionError as e:
|
|
53
|
+
raise AssertionError(f'实际={actual}, 预期=不是空符串') from e
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
@sync_method_callback('ass', '是什么', 4, [
|
|
57
|
+
MethodModel(f='actual', d=True)])
|
|
58
|
+
def p_is_false(actual):
|
|
59
|
+
"""是false"""
|
|
60
|
+
try:
|
|
61
|
+
assert_that(actual).is_false()
|
|
62
|
+
except AssertionError as e:
|
|
63
|
+
raise AssertionError(f'实际={actual}, 预期=是false') from e
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
@sync_method_callback('ass', '是什么', 5, [
|
|
67
|
+
MethodModel(f='actual', d=True)])
|
|
68
|
+
def p_is_true(actual):
|
|
69
|
+
"""是true"""
|
|
70
|
+
try:
|
|
71
|
+
assert_that(actual).is_true()
|
|
72
|
+
except AssertionError as e:
|
|
73
|
+
raise AssertionError(f'实际={actual}, 预期=是true') from e
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
@sync_method_callback('ass', '是什么', 6, [
|
|
77
|
+
MethodModel(f='actual', d=True)])
|
|
78
|
+
def p_is_alpha(actual):
|
|
79
|
+
"""是字母"""
|
|
80
|
+
try:
|
|
81
|
+
assert_that(actual).is_alpha()
|
|
82
|
+
except AssertionError as e:
|
|
83
|
+
raise AssertionError(f'实际={actual}, 预期=是字母') from e
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
@sync_method_callback('ass', '是什么', 7, [
|
|
87
|
+
MethodModel(f='actual', d=True)])
|
|
88
|
+
def p_is_digit(actual):
|
|
89
|
+
"""是数字"""
|
|
90
|
+
try:
|
|
91
|
+
assert_that(actual).is_digit()
|
|
92
|
+
except AssertionError as e:
|
|
93
|
+
raise AssertionError(f'实际={actual}, 预期=是数字') from e
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class WhatIsEqualToAssertion:
|
|
97
|
+
"""等于什么"""
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
@sync_method_callback('ass', '等于什么', 0, [
|
|
101
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
102
|
+
def p_is_equal_to(actual: str, expect: str):
|
|
103
|
+
"""等于expect"""
|
|
104
|
+
try:
|
|
105
|
+
assert_that(actual).is_equal_to(expect),
|
|
106
|
+
except AssertionError as e:
|
|
107
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
@sync_method_callback('ass', '等于什么', 1, [
|
|
111
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
112
|
+
def p_is_not_equal_to(actual: str, expect: str):
|
|
113
|
+
"""不等于expect"""
|
|
114
|
+
try:
|
|
115
|
+
assert_that(actual).is_not_equal_to(expect)
|
|
116
|
+
except AssertionError as e:
|
|
117
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
@sync_method_callback('ass', '等于什么', 2, [
|
|
121
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
122
|
+
def p_is_length(actual: str, expect: str):
|
|
123
|
+
"""长度等于expect"""
|
|
124
|
+
try:
|
|
125
|
+
assert_that(actual).is_length(int(expect))
|
|
126
|
+
except AssertionError as e:
|
|
127
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
@sync_method_callback('ass', '等于什么', 3, [
|
|
131
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
132
|
+
def p_sum_equal_expect(actual: list, expect: str):
|
|
133
|
+
"""长度等于expect"""
|
|
134
|
+
try:
|
|
135
|
+
assert_that(sum(actual)).is_equal_to(expect)
|
|
136
|
+
except AssertionError as e:
|
|
137
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ContainAssertion:
|
|
141
|
+
"""包含什么"""
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
@sync_method_callback('ass', '包含什么', 0, [
|
|
145
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
146
|
+
def p_contains(actual: str, expect: str):
|
|
147
|
+
"""包含expect"""
|
|
148
|
+
try:
|
|
149
|
+
assert_that(actual).contains(expect)
|
|
150
|
+
except AssertionError as e:
|
|
151
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
@sync_method_callback('ass', '包含什么', 1, [
|
|
155
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
156
|
+
def p_is_equal_to_ignoring_case(actual: str, expect: str):
|
|
157
|
+
"""忽略大小写等于expect"""
|
|
158
|
+
try:
|
|
159
|
+
assert_that(actual).is_equal_to_ignoring_case(expect)
|
|
160
|
+
except AssertionError as e:
|
|
161
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
@sync_method_callback('ass', '包含什么', 2, [
|
|
165
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
166
|
+
def p_contains_ignoring_case(actual: str, expect: str):
|
|
167
|
+
"""包含忽略大小写expect"""
|
|
168
|
+
try:
|
|
169
|
+
assert_that(actual).contains_ignoring_case(expect)
|
|
170
|
+
except AssertionError as e:
|
|
171
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
172
|
+
|
|
173
|
+
@staticmethod
|
|
174
|
+
@sync_method_callback('ass', '包含什么', 3, [
|
|
175
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
176
|
+
def p_contains_only(actual: str, expect: str):
|
|
177
|
+
"""仅包含expect"""
|
|
178
|
+
try:
|
|
179
|
+
assert_that(actual).contains_only(expect)
|
|
180
|
+
except AssertionError as e:
|
|
181
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
@sync_method_callback('ass', '包含什么', 4, [
|
|
185
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
186
|
+
def p_does_not_contain(actual: str, expect: str):
|
|
187
|
+
"""不包含expect"""
|
|
188
|
+
try:
|
|
189
|
+
assert_that(actual).does_not_contain(expect)
|
|
190
|
+
except AssertionError as e:
|
|
191
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class MatchingAssertion:
|
|
195
|
+
"""匹配什么"""
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
@sync_method_callback('ass', '匹配什么', 0, [
|
|
199
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
200
|
+
def p_in_dict(actual: dict, expect: dict):
|
|
201
|
+
"""JSON匹配"""
|
|
202
|
+
filtered_actual = filter_dict(dict(actual), dict(expect))
|
|
203
|
+
diff = DeepDiff(filtered_actual, expect, ignore_order=True)
|
|
204
|
+
assert not diff, f'实际={actual}, 预期={expect}'
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
@sync_method_callback('ass', '匹配什么', 1, [
|
|
208
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
209
|
+
def p_is_in(actual: str, expect: str):
|
|
210
|
+
"""在expect里面"""
|
|
211
|
+
try:
|
|
212
|
+
assert_that(actual).is_in(expect)
|
|
213
|
+
except AssertionError as e:
|
|
214
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
@sync_method_callback('ass', '匹配什么', 2, [
|
|
218
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
219
|
+
def p_is_not_in(actual: str, expect: str):
|
|
220
|
+
"""不在expect里面"""
|
|
221
|
+
try:
|
|
222
|
+
assert_that(actual).is_not_in(expect)
|
|
223
|
+
except AssertionError as e:
|
|
224
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
225
|
+
|
|
226
|
+
@staticmethod
|
|
227
|
+
@sync_method_callback('ass', '匹配什么', 3, [
|
|
228
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
229
|
+
def p_starts_with(actual: str, expect: str):
|
|
230
|
+
"""以expect开头"""
|
|
231
|
+
try:
|
|
232
|
+
assert_that(actual).starts_with(expect)
|
|
233
|
+
except AssertionError as e:
|
|
234
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
@sync_method_callback('ass', '匹配什么', 4, [
|
|
238
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
239
|
+
def p_ends_with(actual: str, expect: str):
|
|
240
|
+
"""以expect结尾"""
|
|
241
|
+
try:
|
|
242
|
+
assert_that(actual).ends_with(expect)
|
|
243
|
+
except AssertionError as e:
|
|
244
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
245
|
+
|
|
246
|
+
@staticmethod
|
|
247
|
+
@sync_method_callback('ass', '匹配什么', 5, [
|
|
248
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
249
|
+
def p_matches(actual: str, expect: str):
|
|
250
|
+
"""正则匹配等于expect"""
|
|
251
|
+
try:
|
|
252
|
+
assert_that(actual).matches(expect)
|
|
253
|
+
except AssertionError as e:
|
|
254
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
@sync_method_callback('ass', '匹配什么', 6, [
|
|
258
|
+
MethodModel(f='actual', d=True), MethodModel(f='expect', p='请输入断言值', d=True)])
|
|
259
|
+
def p_does_not_match(actual: str, expect: str):
|
|
260
|
+
"""正则不匹配expect"""
|
|
261
|
+
try:
|
|
262
|
+
assert_that(actual).does_not_match(expect)
|
|
263
|
+
except AssertionError as e:
|
|
264
|
+
raise AssertionError(f'实际={actual}, 预期={expect}') from e
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def filter_dict(actual: dict, expect: dict) -> dict:
|
|
268
|
+
filtered = {}
|
|
269
|
+
for key in expect.keys():
|
|
270
|
+
if key in actual:
|
|
271
|
+
if isinstance(expect[key], dict):
|
|
272
|
+
filtered[key] = filter_dict(actual[key], expect[key])
|
|
273
|
+
elif isinstance(expect[key], list) and isinstance(actual[key], list):
|
|
274
|
+
filtered[key] = []
|
|
275
|
+
for item in actual[key]:
|
|
276
|
+
if isinstance(item, dict):
|
|
277
|
+
filtered_item = filter_dict(item, expect[key][0])
|
|
278
|
+
filtered[key].append(filtered_item)
|
|
279
|
+
else:
|
|
280
|
+
filtered[key].append(item)
|
|
281
|
+
else:
|
|
282
|
+
filtered[key] = actual[key]
|
|
283
|
+
return filtered
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class PublicAssertion(WhatIsItAssertion, ContainAssertion, MatchingAssertion, WhatIsEqualToAssertion):
|
|
287
|
+
pass
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
if __name__ == '__main__':
|
|
291
|
+
PublicAssertion.p_is_equal_to('1', '2')
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Project: 芒果测试平台
|
|
3
|
+
# @Description:
|
|
4
|
+
# @Time : 2023-11-20 9:47
|
|
5
|
+
# @Author : 毛鹏
|
|
6
|
+
|
|
7
|
+
from mangokit.database import MysqlConnect
|
|
8
|
+
from mangokit.decorator import sync_method_callback
|
|
9
|
+
from mangokit.models import MethodModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SqlAssertion:
|
|
13
|
+
"""sql断言"""
|
|
14
|
+
mysql_connect: MysqlConnect = None
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
@sync_method_callback('ass', 'sql断言', 0, [
|
|
18
|
+
MethodModel(f='sql', p='请输入sql语句,确保只会查出一个值', d=True),
|
|
19
|
+
MethodModel(f='expect', p='期望的json', d=True), ])
|
|
20
|
+
async def sql_is_equal(sql: str, expect: list[dict]):
|
|
21
|
+
"""值相等"""
|
|
22
|
+
result = SqlAssertion.mysql_connect.condition_execute(sql)
|
|
23
|
+
assert all(dict2 in result for dict2 in expect), f'实际={result}, 预期=列表个数相等'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == '__main__':
|
|
27
|
+
_sql = "SELECT id,`name`,`status` FROM `project`;"
|
|
28
|
+
_expect = [{'id': 2, 'name': '1CDXP', 'status': 1}, {'id': 5, 'name': 'AIGC', 'status': 1},
|
|
29
|
+
{'id': 10, 'name': 'DESK', 'status': 1}, {'id': 11, 'name': 'AIGC-SaaS', 'status': 1}]
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Project: 芒果测试平台
|
|
3
|
+
# @Description:
|
|
4
|
+
# @Time : 2023-03-07 8:24
|
|
5
|
+
# @Author : 毛鹏
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from ..data_processor._cache_tool import CacheTool
|
|
12
|
+
from ..data_processor._coding_tool import CodingTool
|
|
13
|
+
from ..data_processor._encryption_tool import EncryptionTool
|
|
14
|
+
from ..data_processor._json_tool import JsonTool
|
|
15
|
+
from ..data_processor._random_character_info_data import RandomCharacterInfoData
|
|
16
|
+
from ..data_processor._random_number_data import RandomNumberData
|
|
17
|
+
from ..data_processor._random_string_data import RandomStringData
|
|
18
|
+
from ..data_processor._random_time_data import RandomTimeData
|
|
19
|
+
from ..data_processor._sql_cache import SqlCache
|
|
20
|
+
from ..exceptions import ERROR_MSG_0002, ERROR_MSG_0047, MangoKitError
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
ObtainRandomData类的函数注释必须是: “”“中间写值”“”
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
python_version = sys.version_info
|
|
27
|
+
if f"{python_version.major}.{python_version.minor}" != "3.10":
|
|
28
|
+
raise Exception("必须使用>Python3.10.4")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ObtainRandomData(RandomNumberData, RandomCharacterInfoData, RandomTimeData, RandomStringData):
|
|
32
|
+
""" 获取随机数据 """
|
|
33
|
+
|
|
34
|
+
def regular(self, func: str):
|
|
35
|
+
"""
|
|
36
|
+
反射并执行函数
|
|
37
|
+
:param func: 函数
|
|
38
|
+
:return:
|
|
39
|
+
"""
|
|
40
|
+
match = re.search(r'\((.*?)\)', func)
|
|
41
|
+
if match:
|
|
42
|
+
try:
|
|
43
|
+
content = json.loads(match.group(1))
|
|
44
|
+
if not isinstance(content, dict):
|
|
45
|
+
content = {'data': match.group(1)}
|
|
46
|
+
except json.decoder.JSONDecodeError:
|
|
47
|
+
content = {'data': match.group(1)}
|
|
48
|
+
|
|
49
|
+
func = re.sub(r'\(' + match.group(1) + r'\)', '', func)
|
|
50
|
+
try:
|
|
51
|
+
if content['data'] != '':
|
|
52
|
+
return getattr(self, func)(**content)
|
|
53
|
+
return getattr(self, func)()
|
|
54
|
+
except AttributeError:
|
|
55
|
+
raise MangoKitError(*ERROR_MSG_0047)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DataClean(JsonTool, CacheTool, EncryptionTool, CodingTool):
|
|
59
|
+
"""存储或处理随机数据"""
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class DataProcessor(ObtainRandomData, DataClean):
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
ObtainRandomData.__init__(self)
|
|
67
|
+
DataClean.__init__(self)
|
|
68
|
+
|
|
69
|
+
def replace(self, data: list | dict | str | None) -> list | dict | str | None:
|
|
70
|
+
if not data:
|
|
71
|
+
return data
|
|
72
|
+
if isinstance(data, list):
|
|
73
|
+
return [self.replace(item) for item in data]
|
|
74
|
+
elif isinstance(data, dict):
|
|
75
|
+
return {key: self.replace(value) for key, value in data.items()}
|
|
76
|
+
else:
|
|
77
|
+
return self.replace_str(data)
|
|
78
|
+
|
|
79
|
+
def replace_str(self, data: str) -> str:
|
|
80
|
+
replace_list = re.findall(r"\${{.*?}}", str(data))
|
|
81
|
+
for replace_value in replace_list:
|
|
82
|
+
key_text = self.remove_parentheses(replace_value)
|
|
83
|
+
args = key_text.split(",")
|
|
84
|
+
key_text, key = (args[0], args[1]) if len(args) == 2 else (args[0], None)
|
|
85
|
+
if key and (key_value := self.get_cache(key)):
|
|
86
|
+
return key_value
|
|
87
|
+
|
|
88
|
+
value = self.regular(key_text) if self.identify_parentheses(key_text) else self.get_cache(key_text)
|
|
89
|
+
if value is None:
|
|
90
|
+
raise MangoKitError(*ERROR_MSG_0002, value=(key_text,))
|
|
91
|
+
|
|
92
|
+
if key:
|
|
93
|
+
self.set_cache(key, value)
|
|
94
|
+
data = data.replace(replace_value, str(value))
|
|
95
|
+
return data
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def remove_parentheses(cls, data: str) -> str:
|
|
99
|
+
return data.replace("${{", "").replace("}}", "").strip()
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def identify_parentheses(cls, value: str):
|
|
103
|
+
return re.search(r'\((.*?)\)', str(value))
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def is_extract(cls, string: str) -> bool:
|
|
107
|
+
return bool(re.search(r'\$\{\{.*\}\}', string))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
__all__ = [
|
|
111
|
+
'CacheTool',
|
|
112
|
+
'CodingTool',
|
|
113
|
+
'EncryptionTool',
|
|
114
|
+
'JsonTool',
|
|
115
|
+
'RandomCharacterInfoData',
|
|
116
|
+
'RandomNumberData',
|
|
117
|
+
'RandomStringData',
|
|
118
|
+
'RandomTimeData',
|
|
119
|
+
'ObtainRandomData',
|
|
120
|
+
'DataClean',
|
|
121
|
+
'DataProcessor',
|
|
122
|
+
'SqlCache'
|
|
123
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Project: 芒果测试平台# @Description:
|
|
3
|
+
# @Time : 2023-08-29 10:23
|
|
4
|
+
# @Author : 毛鹏
|
|
5
|
+
|
|
6
|
+
from cachetools import LRUCache
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CacheTool:
|
|
10
|
+
""" 内存缓存 """
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self._cache: LRUCache = LRUCache(maxsize=500)
|
|
14
|
+
|
|
15
|
+
def get_cache(self, key: str) -> any:
|
|
16
|
+
"""得到缓存key的value"""
|
|
17
|
+
return self._cache.get(key)
|
|
18
|
+
|
|
19
|
+
def set_cache(self, key: str, value: any) -> None:
|
|
20
|
+
"""设置一个内容到缓存"""
|
|
21
|
+
self._cache[key] = value
|
|
22
|
+
|
|
23
|
+
def delete_cache(self, key: str) -> None:
|
|
24
|
+
"""删除一个缓存"""
|
|
25
|
+
if key in self._cache:
|
|
26
|
+
del self._cache[key]
|
|
27
|
+
|
|
28
|
+
def clear_cache(self) -> None:
|
|
29
|
+
"""清理所有缓存"""
|
|
30
|
+
self._cache.clear()
|
|
31
|
+
|
|
32
|
+
def has_cache(self, key: str) -> bool:
|
|
33
|
+
"""判断缓存是否存在"""
|
|
34
|
+
return key in self._cache
|
|
35
|
+
|
|
36
|
+
def get_all(self, ) -> dict:
|
|
37
|
+
"""获取全部的缓存数据"""
|
|
38
|
+
return {k: v for k, v in self._cache.items()}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Project: 芒果测试平台
|
|
3
|
+
# @Description:
|
|
4
|
+
# @Time : 2023-08-30 14:14
|
|
5
|
+
# @Author : 毛鹏
|
|
6
|
+
import base64
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CodingTool:
|
|
11
|
+
"""编码"""
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def base64_encode(cls, data: str) -> str:
|
|
15
|
+
"""编码字符串"""
|
|
16
|
+
return base64.b64encode(json.dumps(data).encode('utf-8')).decode('utf-8')
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def response_decoding(cls, data) -> str:
|
|
20
|
+
"""解码字符串"""
|
|
21
|
+
return data.encode('latin-1').decode('unicode_escape').encode('utf-8', 'ignore').decode('utf-8')
|