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

testit-tms / adapters-python / 15827064073

23 Jun 2025 02:29PM UTC coverage: 41.954% (-1.5%) from 43.489%
15827064073

Pull #187

github

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

41 of 72 new or added lines in 1 file covered. (56.94%)

1 existing line in 1 file now uncovered.

1095 of 2610 relevant lines covered (41.95%)

0.85 hits per line

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

58.94
/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_name: 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_name:
2✔
NEW
64
            cls.__properties_file = cli_file_name
×
65

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

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

75
        return cls.__load_file_properties_from_ini(path)
2✔
76

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

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

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

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

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

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

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

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

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

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

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

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

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

120
        return cli_properties
2✔
121

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

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

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

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

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

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

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

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

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

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

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

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

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

177
        return env_properties
2✔
178

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

NEW
267
        return properties
×
268

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

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

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

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

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

289
        return properties
2✔
290

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

296
        return var_name
2✔
297

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

303
        return False
×
304

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

NEW
312
        return True
×
313

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