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

testit-tms / adapters-python / 15825250250

23 Jun 2025 01:14PM UTC coverage: 27.933% (-15.6%) from 43.489%
15825250250

Pull #187

github

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

45 of 73 new or added lines in 1 file covered. (61.64%)

30 existing lines in 1 file now uncovered.

762 of 2728 relevant lines covered (27.93%)

0.81 hits per line

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

60.49
/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
    __properties_file = 'connection_config' + __ini_extension
2✔
18
    __available_extensions = [__toml_extension, __ini_extension]
2✔
19

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

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

UNCOV
29
        properties.update(AppProperties.load_env_properties())
×
30

31
        if option:
×
UNCOV
32
            properties.update(AppProperties.load_cli_properties(option))
×
33

34
        AppProperties.__check_properties(properties)
×
35

UNCOV
36
        return properties
×
37

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

44
        if os.environ.get(f'{cls.__env_prefix}_CONFIG_FILE'):
2✔
NEW
45
            cls.__properties_file = os.environ.get(f'{cls.__env_prefix}_CONFIG_FILE')
×
46

47
        if cli_file_name:
2✔
NEW
48
            cls.__properties_file = cli_file_name
×
49

50
        _, extension = os.path.splitext(cls.__properties_file)
2✔
51
        if extension not in cls.__available_extensions:
2✔
NEW
52
            raise FileNotFoundError(
×
53
                f'{cls.__properties_file} is not a valid file ({_, extension}). Available extensions: {cls.__available_extensions}'
54
            )
55

56
        if extension == cls.__toml_extension:
2✔
NEW
57
            return cls.__load_file_properties_from_toml()
×
58

59
        return cls.__load_file_properties_from_ini()
2✔
60

61
    @classmethod
2✔
62
    def load_cli_properties(cls, option):
2✔
63
        cli_properties = {}
2✔
64

65
        if hasattr(option, 'set_url') and cls.__check_property_value(option.set_url):
2✔
66
            cli_properties['url'] = option.set_url
2✔
67

68
        if hasattr(option, 'set_private_token') and cls.__check_property_value(option.set_private_token):
2✔
69
            cli_properties['privatetoken'] = option.set_private_token
2✔
70

71
        if hasattr(option, 'set_project_id') and cls.__check_property_value(option.set_project_id):
2✔
72
            cli_properties['projectid'] = option.set_project_id
2✔
73

74
        if hasattr(option, 'set_configuration_id') and cls.__check_property_value(option.set_configuration_id):
2✔
75
            cli_properties['configurationid'] = option.set_configuration_id
2✔
76

77
        if hasattr(option, 'set_test_run_id') and cls.__check_property_value(option.set_test_run_id):
2✔
78
            cli_properties['testrunid'] = option.set_test_run_id
2✔
79

80
        if hasattr(option, 'set_test_run_name') and cls.__check_property_value(option.set_test_run_name):
2✔
81
            cli_properties['testrunname'] = option.set_test_run_name
2✔
82

83
        if hasattr(option, 'set_tms_proxy') and cls.__check_property_value(option.set_tms_proxy):
2✔
84
            cli_properties['tmsproxy'] = option.set_tms_proxy
2✔
85

86
        if hasattr(option, 'set_adapter_mode') and cls.__check_property_value(option.set_adapter_mode):
2✔
87
            cli_properties['adaptermode'] = option.set_adapter_mode
2✔
88

89
        if hasattr(option, 'set_cert_validation') and cls.__check_property_value(option.set_cert_validation):
2✔
90
            cli_properties['certvalidation'] = option.set_cert_validation
2✔
91

92
        if hasattr(option, 'set_automatic_creation_test_cases') and cls.__check_property_value(
2✔
93
                option.set_automatic_creation_test_cases):
94
            cli_properties['automaticcreationtestcases'] = option.set_automatic_creation_test_cases
2✔
95

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

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

104
        return cli_properties
2✔
105

106
    @classmethod
2✔
107
    def load_env_properties(cls):
2✔
108
        env_properties = {}
2✔
109

110
        if f'{cls.__env_prefix}_URL' in os.environ.keys() and cls.__check_property_value(
2✔
111
                os.environ.get(f'{cls.__env_prefix}_URL')):
112
            env_properties['url'] = os.environ.get(f'{cls.__env_prefix}_URL')
2✔
113

114
        if f'{cls.__env_prefix}_PRIVATE_TOKEN' in os.environ.keys() and cls.__check_property_value(
2✔
115
                os.environ.get(f'{cls.__env_prefix}_PRIVATE_TOKEN')):
116
            env_properties['privatetoken'] = os.environ.get(f'{cls.__env_prefix}_PRIVATE_TOKEN')
2✔
117

118
        if f'{cls.__env_prefix}_PROJECT_ID' in os.environ.keys() and cls.__check_property_value(
2✔
119
                os.environ.get(f'{cls.__env_prefix}_PROJECT_ID')):
120
            env_properties['projectid'] = os.environ.get(f'{cls.__env_prefix}_PROJECT_ID')
2✔
121

122
        if f'{cls.__env_prefix}_CONFIGURATION_ID' in os.environ.keys() and cls.__check_property_value(
2✔
123
                os.environ.get(f'{cls.__env_prefix}_CONFIGURATION_ID')):
124
            env_properties['configurationid'] = os.environ.get(f'{cls.__env_prefix}_CONFIGURATION_ID')
2✔
125

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

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

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

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

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

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

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

156
        if f'{cls.__env_prefix}_IMPORT_REALTIME' in os.environ.keys() and cls.__check_property_value(
2✔
157
                os.environ.get(f'{cls.__env_prefix}_IMPORT_REALTIME')):
158
            env_properties['importrealtime'] = os.environ.get(
2✔
159
                f'{cls.__env_prefix}_IMPORT_REALTIME')
160

161
        return env_properties
2✔
162

163
    @classmethod
2✔
164
    def __check_properties(cls, properties: dict):
2✔
UNCOV
165
        adapter_mode = properties.get('adaptermode')
×
166

UNCOV
167
        if adapter_mode == AdapterMode.NEW_TEST_RUN:
×
UNCOV
168
            try:
×
UNCOV
169
                uuid.UUID(str(properties.get('testrunid')))
×
UNCOV
170
                logging.error('Adapter mode "2" is enabled. Config should not contains test run id!')
×
UNCOV
171
                raise SystemExit
×
UNCOV
172
            except ValueError:
×
UNCOV
173
                pass
×
174

UNCOV
175
        elif adapter_mode in (
×
176
                AdapterMode.RUN_ALL_TESTS,
177
                AdapterMode.USE_FILTER,
178
                None):
UNCOV
179
            try:
×
UNCOV
180
                uuid.UUID(str(properties.get('testrunid')))
×
UNCOV
181
            except ValueError:
×
182
                logging.error(f'Adapter mode "{adapter_mode if adapter_mode else "0"}" is enabled. '
×
183
                              f'The test run ID is needed, but it was not found!')
184
                raise SystemExit
×
185
        else:
186
            logging.error(f'Unknown adapter mode "{adapter_mode}"!')
×
187
            raise SystemExit
×
188

189
        try:
×
190
            uuid.UUID(str(properties.get('projectid')))
×
UNCOV
191
        except ValueError:
×
192
            logging.error('Project ID was not found!')
×
UNCOV
193
            raise SystemExit
×
194

UNCOV
195
        try:
×
196
            url = urlparse(properties.get('url'))
×
197
            if not all([url.scheme, url.netloc]):
×
198
                raise AttributeError
×
199
        except AttributeError:
×
UNCOV
200
            logging.error('URL is invalid!')
×
201
            raise SystemExit
×
202

203
        if not cls.__check_property_value(properties.get('privatetoken')):
×
204
            logging.error('Private token was not found!')
×
UNCOV
205
            raise SystemExit
×
206

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

213
        if not cls.__check_property_value(properties.get('certvalidation')):
×
214
            properties['certvalidation'] = 'true'
×
215

216
        if not cls.__check_property_value(properties.get('automaticcreationtestcases')):
×
217
            properties['automaticcreationtestcases'] = 'false'
×
218

UNCOV
219
        if not cls.__check_property_value(properties.get('automaticupdationlinkstotestcases')):
×
220
            properties['automaticupdationlinkstotestcases'] = 'false'
×
221

222
        if not cls.__check_property_value(properties.get('importrealtime')):
×
UNCOV
223
            properties['importrealtime'] = 'true'
×
224

225
    @classmethod
2✔
226
    def __load_file_properties_from_toml(cls) -> dict:
2✔
NEW
227
        properties = {}
×
228

NEW
UNCOV
229
        with open(cls.__project_metadata_file, "rb+") as file:
×
NEW
230
            toml_dict = tomli.load(file)
×
231

NEW
UNCOV
232
            if not cls.__check_toml_section(toml_dict, cls.__config_section_name):
×
NEW
233
                logging.error(f'Config section in "{cls.__properties_file}" file was not found!')
×
NEW
234
                raise SystemExit
×
235

NEW
236
            config_section = toml_dict.get(cls.__config_section_name)
×
237

NEW
UNCOV
238
            for key, value in config_section.items():
×
NEW
239
                properties[key.lower()] = cls.__search_in_environ(str(value))
×
240

NEW
UNCOV
241
            if cls.__check_toml_section(toml_dict, cls.__debug_section_name):
×
NEW
242
                debug_section = toml_dict.get(cls.__debug_section_name)
×
243

NEW
244
                for key, value in debug_section.items():
×
NEW
245
                    if key == 'tmsproxy':
×
NEW
246
                        properties['tmsproxy'] = cls.__search_in_environ(str(value))
×
247

NEW
248
                    if key == '__dev':
×
NEW
249
                        properties['logs'] = cls.__search_in_environ(str(value)).lower()
×
250

NEW
251
        cls.__check_token_property(properties)
×
252

NEW
253
        return properties
×
254

255
    @classmethod
2✔
256
    def __load_file_properties_from_ini(cls) -> dict:
2✔
257
        properties = {}
2✔
258
        path = os.path.abspath('')
2✔
259
        root = path[:path.index(os.sep)]
2✔
260

261
        while not os.path.isfile(
2✔
262
                path + os.sep + cls.__properties_file) and path != root:
NEW
263
            path = path[:path.rindex(os.sep)]
×
264

265
        path = path + os.sep + cls.__properties_file
2✔
266

267
        if os.path.isfile(path):
2✔
268
            parser = configparser.RawConfigParser()
2✔
269

270
            parser.read(path, encoding="utf-8")
2✔
271

272
            if parser.has_section(cls.__config_section_name):
2✔
273
                for key, value in parser.items(cls.__config_section_name):
2✔
274
                    properties[key] = cls.__search_in_environ(value)
2✔
275

276
            if parser.has_section('debug'):
2✔
277
                if parser.has_option('debug', 'tmsproxy'):
2✔
278
                    properties['tmsproxy'] = cls.__search_in_environ(
2✔
279
                        parser.get('debug', 'tmsproxy'))
280

281
                if parser.has_option('debug', '__dev'):
2✔
282
                    properties['logs'] = cls.__search_in_environ(
2✔
283
                        parser.get('debug', '__dev')).lower()
284

285
        cls.__check_token_property(properties)
2✔
286

287
        return properties
2✔
288

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

294
        return var_name
2✔
295

296
    @staticmethod
2✔
297
    def __check_property_value(value: str):
2✔
298
        if value is not None and value != "":
2✔
299
            return True
2✔
300

UNCOV
301
        return False
×
302

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

NEW
310
        return True
×
311

312
    @staticmethod
2✔
313
    def __check_token_property(properties: dict):
2✔
314
        if 'privatetoken' in properties:
2✔
315
            warnings.warn(
2✔
316
                'The configuration file specifies a private token. It is not safe.'
317
                ' Use TMS_PRIVATE_TOKEN environment variable',
318
                category=Warning,
319
                stacklevel=2)
320
            warnings.simplefilter('default', Warning)
2✔
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