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

chkware / cli / 7345480911

28 Dec 2023 07:30AM UTC coverage: 88.897% (-1.2%) from 90.093%
7345480911

push

github

web-flow
Fix: Broken CI fix - Coverage report broken (#284)

* ci: update deps versions

* fix: Attribute error for function having command

* refactor: change to function attribute

* refactor: bring grouped command on same statement

* refactor: update coverallsapp action

3 of 3 new or added lines in 1 file covered. (100.0%)

61 existing lines in 6 files now uncovered.

1209 of 1360 relevant lines covered (88.9%)

0.89 hits per line

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

82.91
/chk/modules/validate/assertion_services.py
1
"""
2
Assertion services
3
"""
4

5
import dataclasses
1✔
6
import typing
1✔
7
import uuid
1✔
8
from collections import UserDict
1✔
9
from collections.abc import Callable
1✔
10
from datetime import datetime
1✔
11

12
import chk.modules.validate.assertion_function as asrt_f
1✔
13
from chk.infrastructure.helper import Cast
1✔
14
from chk.infrastructure.templating import StrTemplate
1✔
15
from chk.modules.validate.assertion_message import get_assert_msg_for
1✔
16
from chk.modules.validate.assertion_validation import AssertionEntityType
1✔
17

18
MAP_TYPE_TO_FN: dict[str, Callable] = {
1✔
19
    AssertionEntityType.Accepted: asrt_f.accepted,
20
    AssertionEntityType.Declined: asrt_f.declined,
21
    AssertionEntityType.Equal: asrt_f.equal,
22
    AssertionEntityType.NotEqual: asrt_f.not_equal,
23
    AssertionEntityType.Empty: asrt_f.empty,
24
    AssertionEntityType.NotEmpty: asrt_f.not_empty,
25
    AssertionEntityType.Boolean: asrt_f.boolean,
26
    AssertionEntityType.Integer: asrt_f.integer,
27
    AssertionEntityType.IntegerBetween: asrt_f.integer_between,
28
    AssertionEntityType.IntegerGreater: asrt_f.integer_greater,
29
    AssertionEntityType.IntegerGreaterOrEqual: asrt_f.integer_greater_or_equal,
30
    AssertionEntityType.IntegerLess: asrt_f.integer_less,
31
    AssertionEntityType.IntegerLessOrEqual: asrt_f.integer_less_or_equal,
32
    AssertionEntityType.Float: asrt_f.float_,
33
    AssertionEntityType.FloatBetween: asrt_f.float_between,
34
    AssertionEntityType.FloatGreater: asrt_f.float_greater,
35
    AssertionEntityType.FloatGreaterOrEqual: asrt_f.float_greater_or_equal,
36
    AssertionEntityType.FloatLess: asrt_f.float_less,
37
    AssertionEntityType.FloatLessOrEqual: asrt_f.float_less_or_equal,
38
    AssertionEntityType.Str: asrt_f.str_,
39
    AssertionEntityType.StrHave: asrt_f.str_have,
40
    AssertionEntityType.StrDoNotHave: asrt_f.str_do_not_have,
41
    AssertionEntityType.StrStartsWith: asrt_f.str_starts_with,
42
    AssertionEntityType.StrDoNotStartsWith: asrt_f.str_do_not_starts_with,
43
    AssertionEntityType.StrEndsWith: asrt_f.str_ends_with,
44
    AssertionEntityType.StrDoNotEndsWith: asrt_f.str_do_not_ends_with,
45
    AssertionEntityType.Date: asrt_f.date,
46
    AssertionEntityType.DateAfter: asrt_f.date_after,
47
    AssertionEntityType.DateAfterOrEqual: asrt_f.date_after_or_equal,
48
    AssertionEntityType.DateBefore: asrt_f.date_before,
49
    AssertionEntityType.DateBeforeOrEqual: asrt_f.date_before_or_equal,
50
    AssertionEntityType.List: asrt_f.list_,
51
    AssertionEntityType.ListContains: asrt_f.list_contains,
52
    AssertionEntityType.ListDoNotContains: asrt_f.list_do_not_contains,
53
    AssertionEntityType.ListHasIndex: asrt_f.list_has_index,
54
    AssertionEntityType.ListDoNotHasIndex: asrt_f.list_do_not_has_index,
55
    AssertionEntityType.Map: asrt_f.map_,
56
    AssertionEntityType.MapKeyCount: asrt_f.map_key_count,
57
    AssertionEntityType.MapHasKeys: asrt_f.map_has_keys,
58
    AssertionEntityType.MapDoNotHasKeys: asrt_f.map_do_not_has_keys,
59
    AssertionEntityType.MapExactKeys: asrt_f.map_exact_keys,
60
    AssertionEntityType.Count: asrt_f.count,
61
}
62

63

64
@dataclasses.dataclass
1✔
65
class AssertionEntry:
1✔
66
    """AssertionEntry holds one assertion operation"""
67

68
    assert_type: str
1✔
69
    actual: typing.Any
1✔
70
    expected: typing.Any
1✔
71
    msg_pass: str = dataclasses.field(default_factory=str)
1✔
72
    msg_fail: str = dataclasses.field(default_factory=str)
1✔
73
    cast_actual_to: str = dataclasses.field(default_factory=str)
1✔
74
    actual_given: typing.Any = dataclasses.field(default=NotImplemented)
1✔
75
    actual_b4_cast: typing.Any = dataclasses.field(default=NotImplemented)
1✔
76
    extra_fields: dict = dataclasses.field(default_factory=dict)
1✔
77

78
    @property
1✔
79
    def as_dict(self) -> dict:
1✔
80
        """Return dict representation"""
81

82
        if self.actual_given == NotImplemented:
1✔
83
            self.actual_given = ""
1✔
84
        if self.actual_b4_cast == NotImplemented:
1✔
85
            self.actual_b4_cast = ""
1✔
86
        if self.expected == NotImplemented:
1✔
UNCOV
87
            self.expected = ""
×
88

89
        return dataclasses.asdict(self)
1✔
90

91

92
class SingleTestRunResult(UserDict):
1✔
93
    """Result of an assertion run
94

95
    keys: is_pass, message, assert_used
96
    """
97

98
    @property
1✔
99
    def as_dict(self) -> dict:
1✔
100
        """Convert SingleTestRunResult to a dict"""
101

102
        return {
1✔
103
            key: value.as_dict if key == "assert_used" else value
104
            for key, value in self.items()
105
        }
106

107
    @property
1✔
108
    def as_fmt_str(self) -> str:
1✔
109
        """String representation of ApiResponse
110

111
        Returns:
112
            str: String representation
113
        """
114

115
        return (
1✔
116
            "\n"
117
            f"{'+' if self['is_pass'] else '-'} {self['assert_used'].assert_type} "
118
            + f"{'PASSED' if self['is_pass'] else 'FAILED'}, {self['message']}"
119
        )
120

121

122
class AllTestRunResult(UserDict):
1✔
123
    """Result of a test run
124

125
    keys: id, time_start, time_end, count_all, results, count_fail
126
    """
127

128
    @property
1✔
129
    def as_dict(self) -> dict:
1✔
130
        """Convert AllTestRunResult to a dict"""
131
        _as_dict: dict = {
1✔
132
            key: value for key, value in self.items() if not key.startswith("time_")
133
        }
134

135
        _as_dict |= {
1✔
136
            key: value.timestamp()
137
            for key, value in self.items()
138
            if key.startswith("time_")
139
        }
140

141
        if len(self["results"]) > 0:
1✔
142
            _as_dict["results"] = [
1✔
143
                test_result.as_dict
144
                for test_result in self["results"]
145
                if isinstance(test_result, SingleTestRunResult)
146
            ]
147

148
        return _as_dict
1✔
149

150
    @property
1✔
151
    def as_fmt_str(self) -> str:
1✔
152
        """String representation of ApiResponse
153

154
        Returns:
155
            str: String representation
156
        """
157

158
        _display = (
1✔
159
            f"Test run id: {self['id']}, time taken {self['time_end'] - self['time_start']}\n"
160
            + f"Total tests: {self['count_all']}, "
161
            + f"Total tests failed: {self['count_fail']}\n"
162
        )
163
        _display += "\n> Test run result(s):"
1✔
164

165
        for one_result in self["results"]:
1✔
166
            _display += one_result.as_fmt_str
1✔
167

168
        return _display
1✔
169

170

171
class AssertionEntryListRunner:
1✔
172
    """AssertionAntiquary is service class that run assertion"""
173

174
    @staticmethod
1✔
175
    def _replace_assertion_values(
1✔
176
        assert_item: AssertionEntry, variable_d: dict
177
    ) -> AssertionEntry:
178
        """Replace value for actual and expected data
179

180
        Args:
181
            assert_item: AssertionEntry
182
            variable_d: dict
183
        Returns:
184
            AssertionEntry
185
        """
186

187
        # replace actual value for template
188
        if isinstance(assert_item.actual, str) and StrTemplate.is_tpl(
1✔
189
            assert_item.actual
190
        ):
191
            assert_item.actual_given = assert_item.actual
1✔
192
            str_tpl = StrTemplate(assert_item.actual)
1✔
193
            assert_item.actual = str_tpl.substitute(variable_d)
1✔
194

195
        # convert actual value type
196
        if assert_item.cast_actual_to != "" and isinstance(assert_item.actual, str):
1✔
UNCOV
197
            assert_item.actual_b4_cast = assert_item.actual
×
198

199
            if assert_item.cast_actual_to == "int_or_flot":
×
200
                assert_item.actual = Cast.to_int_or_float(assert_item.actual)
×
201
            elif assert_item.cast_actual_to == "int":
×
202
                assert_item.actual = Cast.to_int(assert_item.actual)
×
203
            elif assert_item.cast_actual_to == "float":
×
204
                assert_item.actual = Cast.to_float(assert_item.actual)
×
205
            elif assert_item.cast_actual_to == "bool":
×
206
                assert_item.actual = Cast.to_bool(assert_item.actual)
×
207
            elif assert_item.cast_actual_to == "none":
×
208
                assert_item.actual = Cast.to_none(assert_item.actual)
×
209
            elif assert_item.cast_actual_to in ["map", "list", "str"]:
×
210
                assert_item.actual = Cast.to_hashable(assert_item.actual)
×
211
            elif assert_item.cast_actual_to == "auto":
×
UNCOV
212
                assert_item.actual = Cast.to_auto(assert_item.actual)
×
213

214
        # replace expected value for template
215
        if isinstance(assert_item.expected, str) and StrTemplate.is_tpl(
1✔
216
            assert_item.expected
217
        ):
218
            str_tpl = StrTemplate(assert_item.expected)
1✔
219
            assert_item.expected = str_tpl.substitute(variable_d)
1✔
220

221
        return assert_item
1✔
222

223
    @staticmethod
1✔
224
    def _prepare_test_run_result(
1✔
225
        resp: SingleTestRunResult,
226
        assert_item: AssertionEntry,
227
        asrt_resp: ValueError | bool,
228
    ) -> None:
229
        def _prepare_message_values() -> dict:
1✔
230
            return {
1✔
231
                "assert_type": assert_item.assert_type,
232
                "type_actual": assert_item.actual.__class__.__name__,
233
                "type_expected": assert_item.expected.__class__.__name__,
234
                "value_actual": assert_item.actual,
235
                "value_expected": assert_item.expected,
236
                "value_actual_given": assert_item.actual_given,
237
                "value_actual_b4_cast": assert_item.actual_b4_cast,
238
                "extra_fields": assert_item.extra_fields,
239
            }
240

241
        def _prepare_message() -> str:
1✔
242
            if isinstance(asrt_resp, ValueError):
1✔
UNCOV
243
                return get_assert_msg_for(f"{asrt_fn_name}.{str(asrt_resp)}").format(
×
244
                    **_prepare_message_values()
245
                )
246

247
            if asrt_resp:
1✔
248
                message = (
1✔
249
                    get_assert_msg_for(f"{asrt_fn_name}.pass")
250
                    if assert_item.msg_pass == ""
251
                    else assert_item.msg_pass
252
                )
253
            else:
254
                message = (
1✔
255
                    get_assert_msg_for(f"{asrt_fn_name}.fail")
256
                    if assert_item.msg_fail == ""
257
                    else assert_item.msg_fail
258
                )
259

260
            return message.format(**_prepare_message_values())
1✔
261

262
        asrt_fn_name = MAP_TYPE_TO_FN[assert_item.assert_type].__name__
1✔
263

264
        if isinstance(asrt_resp, ValueError):
1✔
265
            resp["is_pass"] = False
×
UNCOV
266
            resp["message"] = _prepare_message()
×
267
        else:
268
            resp["is_pass"] = asrt_resp
1✔
269
            resp["message"] = _prepare_message()
1✔
270

271
    @staticmethod
1✔
272
    def _call_assertion_method(
1✔
273
        assert_item: AssertionEntry,
274
    ) -> ValueError | bool:
275
        """Call assertion method
276

277
        Args:
278
            assert_item: AssertionEntry
279
        Returns:
280
            ValueError | bool
281
        """
282

283
        asrt_fn = MAP_TYPE_TO_FN[assert_item.assert_type]
1✔
284
        return asrt_fn(**assert_item.as_dict)
1✔
285

286
    @staticmethod
1✔
287
    def test_run(
1✔
288
        assert_list: list[AssertionEntry], variables: dict
289
    ) -> AllTestRunResult:
290
        """Run the tests
291

292
        Args:
293
            assert_list: list[AssertionEntry]
294
            variables: dict
295

296
        Returns:
297
            AllTestRunResult: Test run result
298
        """
299

300
        test_run_result = AllTestRunResult(
1✔
301
            id=str(uuid.uuid4()),
302
            time_start=datetime.now(),
303
            count_all=len(assert_list),
304
            count_fail=0,
305
        )
306

307
        results: list[SingleTestRunResult] = []
1✔
308

309
        for assert_item in assert_list:
1✔
310
            assert_item = AssertionEntryListRunner._replace_assertion_values(
1✔
311
                assert_item, variables
312
            )
313

314
            resp = SingleTestRunResult(assert_used=assert_item)
1✔
315
            asrt_resp = AssertionEntryListRunner._call_assertion_method(assert_item)
1✔
316

317
            AssertionEntryListRunner._prepare_test_run_result(
1✔
318
                resp, assert_item, asrt_resp
319
            )
320

321
            if resp["is_pass"] is False:
1✔
UNCOV
322
                test_run_result["count_fail"] += 1
×
323

324
            results.append(resp)
1✔
325

326
        test_run_result["time_end"] = datetime.now()
1✔
327
        test_run_result["results"] = results
1✔
328

329
        return test_run_result
1✔
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