• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

testit-tms / adapters-python / 15827179693

23 Jun 2025 02:34PM UTC coverage: 35.139% (-8.4%) from 43.489%
15827179693

Pull #187

github

web-flow
Merge 6baec3f57 into 5b0ba34d2
Pull Request #187: fix: TMS-33439: fix parsing the pyproject.toml.

41 of 73 new or added lines in 1 file covered. (56.16%)

1 existing line in 1 file now uncovered.

1103 of 3139 relevant lines covered (35.14%)

0.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.65
/testit-python-commons/src/testit_python_commons/app_properties.py
1
import configparser
2✔
2
import logging
2✔
3
import os
2✔
4
import warnings
2✔
5
import uuid
2✔
6
import re
2✔
7
from urllib.parse import urlparse
2✔
8
import tomli
2✔
9

10
from testit_python_commons.models.adapter_mode import AdapterMode
2✔
11

12

13
class AppProperties:
2✔
14
    __toml_extension = '.toml'
2✔
15
    __ini_extension = '.ini'
2✔
16
    __project_metadata_file = 'pyproject' + __toml_extension
2✔
17
    __tms_config_file = 'connection_config' + __ini_extension
2✔
18
    __properties_file = __tms_config_file
2✔
19
    __available_extensions = [__toml_extension, __ini_extension]
2✔
20

21
    __env_prefix = 'TMS'
2✔
22
    __config_section_name = 'testit'
2✔
23
    __debug_section_name = 'debug'
2✔
24

25
    @staticmethod
2✔
26
    def load_properties(option=None):
2✔
27
        properties = AppProperties.load_file_properties(
×
28
            option.set_config_file if hasattr(option, 'set_config_file') else None)
29

NEW
30
        AppProperties.__check_token_property(properties)
×
31

UNCOV
32
        properties.update(AppProperties.load_env_properties())
×
33

34
        if option:
×
35
            properties.update(AppProperties.load_cli_properties(option))
×
36

37
        AppProperties.__check_properties(properties)
×
38

39
        return properties
×
40

41
    @classmethod
2✔
42
    def load_file_properties(cls, cli_file_path: str = None):
2✔
43
        if os.path.isfile(cls.__project_metadata_file):
2✔
44
            # https://peps.python.org/pep-0621/
45
            cls.__properties_file = cls.__project_metadata_file
×
46

47
        path = os.path.abspath('')
2✔
48
        root = path[:path.index(os.sep)]
2✔
49

50
        while not os.path.isfile(
2✔
51
                path + os.sep + cls.__tms_config_file) and path != root:
52
            path = path[:path.rindex(os.sep)]
×
53

54
        path = path + os.sep + cls.__tms_config_file
2✔
55

56
        if os.path.isfile(path):
2✔
57
            cls.__properties_file = cls.__tms_config_file
2✔
58

59
        if os.environ.get(f'{cls.__env_prefix}_CONFIG_FILE'):
2✔
NEW
60
            cls.__properties_file = cls.__tms_config_file
×
NEW
61
            path = os.environ.get(f'{cls.__env_prefix}_CONFIG_FILE')
×
62

63
        if cli_file_path:
2✔
NEW
64
            cls.__properties_file = cls.__tms_config_file
×
NEW
65
            path = cli_file_path
×
66

67
        _, extension = os.path.splitext(cls.__properties_file)
2✔
68
        if extension not in cls.__available_extensions:
2✔
NEW
69
            raise FileNotFoundError(
×
70
                f'{cls.__properties_file} is not a valid file ({_, extension}). Available extensions: {cls.__available_extensions}'
71
            )
72

73
        if extension == cls.__toml_extension:
2✔
NEW
74
            return cls.__load_file_properties_from_toml()
×
75

76
        return cls.__load_file_properties_from_ini(path)
2✔
77

78
    @classmethod
2✔
79
    def load_cli_properties(cls, option):
2✔
80
        cli_properties = {}
2✔
81

82
        if hasattr(option, 'set_url') and cls.__check_property_value(option.set_url):
2✔
83
            cli_properties['url'] = option.set_url
2✔
84

85
        if hasattr(option, 'set_private_token') and cls.__check_property_value(option.set_private_token):
2✔
86
            cli_properties['privatetoken'] = option.set_private_token
2✔
87

88
        if hasattr(option, 'set_project_id') and cls.__check_property_value(option.set_project_id):
2✔
89
            cli_properties['projectid'] = option.set_project_id
2✔
90

91
        if hasattr(option, 'set_configuration_id') and cls.__check_property_value(option.set_configuration_id):
2✔
92
            cli_properties['configurationid'] = option.set_configuration_id
2✔
93

94
        if hasattr(option, 'set_test_run_id') and cls.__check_property_value(option.set_test_run_id):
2✔
95
            cli_properties['testrunid'] = option.set_test_run_id
2✔
96

97
        if hasattr(option, 'set_test_run_name') and cls.__check_property_value(option.set_test_run_name):
2✔
98
            cli_properties['testrunname'] = option.set_test_run_name
2✔
99

100
        if hasattr(option, 'set_tms_proxy') and cls.__check_property_value(option.set_tms_proxy):
2✔
101
            cli_properties['tmsproxy'] = option.set_tms_proxy
2✔
102

103
        if hasattr(option, 'set_adapter_mode') and cls.__check_property_value(option.set_adapter_mode):
2✔
104
            cli_properties['adaptermode'] = option.set_adapter_mode
2✔
105

106
        if hasattr(option, 'set_cert_validation') and cls.__check_property_value(option.set_cert_validation):
2✔
107
            cli_properties['certvalidation'] = option.set_cert_validation
2✔
108

109
        if hasattr(option, 'set_automatic_creation_test_cases') and cls.__check_property_value(
2✔
110
                option.set_automatic_creation_test_cases):
111
            cli_properties['automaticcreationtestcases'] = option.set_automatic_creation_test_cases
2✔
112

113
        if hasattr(option, 'set_automatic_updation_links_to_test_cases') and cls.__check_property_value(
2✔
114
                option.set_automatic_updation_links_to_test_cases):
115
            cli_properties['automaticupdationlinkstotestcases'] = option.set_automatic_updation_links_to_test_cases
2✔
116

117
        if hasattr(option, 'set_import_realtime') and cls.__check_property_value(
2✔
118
                option.set_import_realtime):
119
            cli_properties['importrealtime'] = option.set_import_realtime
2✔
120

121
        return cli_properties
2✔
122

123
    @classmethod
2✔
124
    def load_env_properties(cls):
2✔
125
        env_properties = {}
2✔
126

127
        if f'{cls.__env_prefix}_URL' in os.environ.keys() and cls.__check_property_value(
2✔
128
                os.environ.get(f'{cls.__env_prefix}_URL')):
129
            env_properties['url'] = os.environ.get(f'{cls.__env_prefix}_URL')
2✔
130

131
        if f'{cls.__env_prefix}_PRIVATE_TOKEN' in os.environ.keys() and cls.__check_property_value(
2✔
132
                os.environ.get(f'{cls.__env_prefix}_PRIVATE_TOKEN')):
133
            env_properties['privatetoken'] = os.environ.get(f'{cls.__env_prefix}_PRIVATE_TOKEN')
2✔
134

135
        if f'{cls.__env_prefix}_PROJECT_ID' in os.environ.keys() and cls.__check_property_value(
2✔
136
                os.environ.get(f'{cls.__env_prefix}_PROJECT_ID')):
137
            env_properties['projectid'] = os.environ.get(f'{cls.__env_prefix}_PROJECT_ID')
2✔
138

139
        if f'{cls.__env_prefix}_CONFIGURATION_ID' in os.environ.keys() and cls.__check_property_value(
2✔
140
                os.environ.get(f'{cls.__env_prefix}_CONFIGURATION_ID')):
141
            env_properties['configurationid'] = os.environ.get(f'{cls.__env_prefix}_CONFIGURATION_ID')
2✔
142

143
        if f'{cls.__env_prefix}_TEST_RUN_ID' in os.environ.keys() and cls.__check_property_value(
2✔
144
                os.environ.get(f'{cls.__env_prefix}_TEST_RUN_ID')):
145
            env_properties['testrunid'] = os.environ.get(f'{cls.__env_prefix}_TEST_RUN_ID')
2✔
146

147
        if f'{cls.__env_prefix}_TEST_RUN_NAME' in os.environ.keys() and cls.__check_property_value(
2✔
148
                os.environ.get(f'{cls.__env_prefix}_TEST_RUN_NAME')):
149
            env_properties['testrunname'] = os.environ.get(f'{cls.__env_prefix}_TEST_RUN_NAME')
2✔
150

151
        if f'{cls.__env_prefix}_PROXY' in os.environ.keys() and cls.__check_property_value(
2✔
152
                os.environ.get(f'{cls.__env_prefix}_PROXY')):
153
            env_properties['tmsproxy'] = os.environ.get(f'{cls.__env_prefix}_PROXY')
2✔
154

155
        if f'{cls.__env_prefix}_ADAPTER_MODE' in os.environ.keys() and cls.__check_property_value(
2✔
156
                os.environ.get(f'{cls.__env_prefix}_ADAPTER_MODE')):
157
            env_properties['adaptermode'] = os.environ.get(f'{cls.__env_prefix}_ADAPTER_MODE')
2✔
158

159
        if f'{cls.__env_prefix}_CERT_VALIDATION' in os.environ.keys() and cls.__check_property_value(
2✔
160
                os.environ.get(f'{cls.__env_prefix}_CERT_VALIDATION')):
161
            env_properties['certvalidation'] = os.environ.get(f'{cls.__env_prefix}_CERT_VALIDATION')
2✔
162

163
        if f'{cls.__env_prefix}_AUTOMATIC_CREATION_TEST_CASES' in os.environ.keys() and cls.__check_property_value(
2✔
164
                os.environ.get(f'{cls.__env_prefix}_AUTOMATIC_CREATION_TEST_CASES')):
165
            env_properties['automaticcreationtestcases'] = os.environ.get(
2✔
166
                f'{cls.__env_prefix}_AUTOMATIC_CREATION_TEST_CASES')
167

168
        if f'{cls.__env_prefix}_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES' in os.environ.keys() and cls.__check_property_value(
2✔
169
                os.environ.get(f'{cls.__env_prefix}_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES')):
170
            env_properties['automaticupdationlinkstotestcases'] = os.environ.get(
2✔
171
                f'{cls.__env_prefix}_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES')
172

173
        if f'{cls.__env_prefix}_IMPORT_REALTIME' in os.environ.keys() and cls.__check_property_value(
2✔
174
                os.environ.get(f'{cls.__env_prefix}_IMPORT_REALTIME')):
175
            env_properties['importrealtime'] = os.environ.get(
2✔
176
                f'{cls.__env_prefix}_IMPORT_REALTIME')
177

178
        return env_properties
2✔
179

180
    @classmethod
2✔
181
    def __check_properties(cls, properties: dict):
2✔
182
        adapter_mode = properties.get('adaptermode')
×
183

184
        if adapter_mode == AdapterMode.NEW_TEST_RUN:
×
185
            try:
×
186
                uuid.UUID(str(properties.get('testrunid')))
×
187
                logging.error('Adapter mode "2" is enabled. Config should not contains test run id!')
×
188
                raise SystemExit
×
189
            except ValueError:
×
190
                pass
×
191

192
        elif adapter_mode in (
×
193
                AdapterMode.RUN_ALL_TESTS,
194
                AdapterMode.USE_FILTER,
195
                None):
196
            try:
×
197
                uuid.UUID(str(properties.get('testrunid')))
×
198
            except ValueError:
×
199
                logging.error(f'Adapter mode "{adapter_mode if adapter_mode else "0"}" is enabled. '
×
200
                              f'The test run ID is needed, but it was not found!')
201
                raise SystemExit
×
202
        else:
203
            logging.error(f'Unknown adapter mode "{adapter_mode}"!')
×
204
            raise SystemExit
×
205

206
        try:
×
207
            uuid.UUID(str(properties.get('projectid')))
×
208
        except ValueError:
×
209
            logging.error('Project ID was not found!')
×
210
            raise SystemExit
×
211

212
        try:
×
213
            url = urlparse(properties.get('url'))
×
214
            if not all([url.scheme, url.netloc]):
×
215
                raise AttributeError
×
216
        except AttributeError:
×
217
            logging.error('URL is invalid!')
×
218
            raise SystemExit
×
219

220
        if not cls.__check_property_value(properties.get('privatetoken')):
×
221
            logging.error('Private token was not found!')
×
222
            raise SystemExit
×
223

224
        try:
×
225
            uuid.UUID(str(properties.get('configurationid')))
×
226
        except ValueError:
×
227
            logging.error('Configuration ID was not found!')
×
228
            raise SystemExit
×
229

230
        if not cls.__check_property_value(properties.get('certvalidation')):
×
231
            properties['certvalidation'] = 'true'
×
232

233
        if not cls.__check_property_value(properties.get('automaticcreationtestcases')):
×
234
            properties['automaticcreationtestcases'] = 'false'
×
235

236
        if not cls.__check_property_value(properties.get('automaticupdationlinkstotestcases')):
×
237
            properties['automaticupdationlinkstotestcases'] = 'false'
×
238

239
        if not cls.__check_property_value(properties.get('importrealtime')):
×
240
            properties['importrealtime'] = 'true'
×
241

242
    @classmethod
2✔
243
    def __load_file_properties_from_toml(cls) -> dict:
2✔
NEW
244
        properties = {}
×
245

NEW
246
        with open(cls.__project_metadata_file, "rb+") as file:
×
NEW
247
            toml_dict = tomli.load(file)
×
248

NEW
249
            if not cls.__check_toml_section(toml_dict, cls.__config_section_name):
×
NEW
250
                logging.error(f'Config section in "{cls.__properties_file}" file was not found!')
×
NEW
251
                raise SystemExit
×
252

NEW
253
            config_section = toml_dict.get(cls.__config_section_name)
×
254

NEW
255
            for key, value in config_section.items():
×
NEW
256
                properties[key.lower()] = cls.__search_in_environ(str(value))
×
257

NEW
258
            if cls.__check_toml_section(toml_dict, cls.__debug_section_name):
×
NEW
259
                debug_section = toml_dict.get(cls.__debug_section_name)
×
260

NEW
261
                for key, value in debug_section.items():
×
NEW
262
                    if key == 'tmsproxy':
×
NEW
263
                        properties['tmsproxy'] = cls.__search_in_environ(str(value))
×
264

NEW
265
                    if key == '__dev':
×
NEW
266
                        properties['logs'] = cls.__search_in_environ(str(value)).lower()
×
267

NEW
268
        return properties
×
269

270
    @classmethod
2✔
271
    def __load_file_properties_from_ini(cls, path: str) -> dict:
2✔
272
        properties = {}
2✔
273
        parser = configparser.RawConfigParser()
2✔
274

275
        parser.read(path, encoding="utf-8")
2✔
276

277
        if parser.has_section(cls.__config_section_name):
2✔
278
            for key, value in parser.items(cls.__config_section_name):
2✔
279
                properties[key] = cls.__search_in_environ(value)
2✔
280

281
        if parser.has_section('debug'):
2✔
282
            if parser.has_option('debug', 'tmsproxy'):
2✔
283
                properties['tmsproxy'] = cls.__search_in_environ(
2✔
284
                    parser.get('debug', 'tmsproxy'))
285

286
            if parser.has_option('debug', '__dev'):
2✔
287
                properties['logs'] = cls.__search_in_environ(
2✔
288
                    parser.get('debug', '__dev')).lower()
289

290
        return properties
2✔
291

292
    @staticmethod
2✔
293
    def __search_in_environ(var_name: str):
2✔
294
        if re.fullmatch(r'{[a-zA-Z_]\w*}', var_name) and var_name[1:-1] in os.environ:
2✔
295
            return os.environ[var_name[1:-1]]
×
296

297
        return var_name
2✔
298

299
    @staticmethod
2✔
300
    def __check_property_value(value: str):
2✔
301
        if value is not None and value != "":
2✔
302
            return True
2✔
303

304
        return False
×
305

306
    @staticmethod
2✔
307
    def __check_toml_section(toml_dict: dict, section_name: str) -> bool:
2✔
NEW
308
        if section_name not in toml_dict.keys():
×
NEW
309
            return False
×
NEW
310
        if not type(toml_dict.get(section_name)) is dict:
×
NEW
311
            return False
×
312

NEW
313
        return True
×
314

315
    @staticmethod
2✔
316
    def __check_token_property(properties: dict):
2✔
NEW
317
        if 'privatetoken' in properties:
×
NEW
318
            warnings.warn(
×
319
                'The configuration file specifies a private token. It is not safe.'
320
                ' Use TMS_PRIVATE_TOKEN environment variable',
321
                category=Warning,
322
                stacklevel=2)
NEW
323
            warnings.simplefilter('default', Warning)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc