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

chkware / cli / 5994836773

28 Aug 2023 02:17AM UTC coverage: 86.886% (-0.009%) from 86.895%
5994836773

push

github

web-flow
Merge pull request #241 from 0hsn/fix/json-nf-NotImple

Fix: json conversion issue in `--nf` option with `NotImplemented` values in AssertItems

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

2034 of 2341 relevant lines covered (86.89%)

0.87 hits per line

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

82.57
/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.modules.validate.assertion_message import get_assert_msg_for
1✔
15
from chk.infrastructure.symbol_table import linear_replace
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✔
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 (
1✔
189
            isinstance(assert_item.actual, str)
190
            and "{{" in assert_item.actual
191
            and "}}" in assert_item.actual
192
        ):
193
            assert_item.actual_given = assert_item.actual
1✔
194
            assert_item.actual = linear_replace(assert_item.actual, variable_d)
1✔
195

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

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

215
        # replace expected value for template
216
        if (
1✔
217
            isinstance(assert_item.expected, str)
218
            and "{{" in assert_item.expected
219
            and "}}" in assert_item.expected
220
        ):
221
            assert_item.expected = linear_replace(assert_item.expected, variable_d)
1✔
222

223
        return assert_item
1✔
224

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

243
        asrt_fn_name = MAP_TYPE_TO_FN[assert_item.assert_type].__name__
1✔
244

245
        if isinstance(asrt_resp, ValueError):
1✔
246
            resp["is_pass"] = False
×
247
            resp["message"] = get_assert_msg_for(
×
248
                f"{asrt_fn_name}.{str(asrt_resp)}"
249
            ).format(**_prepare_message_values())
250
        else:
251
            resp["is_pass"] = asrt_resp
1✔
252

253
            message = (
1✔
254
                get_assert_msg_for(f"{asrt_fn_name}.pass")
255
                if asrt_resp
256
                else get_assert_msg_for(f"{asrt_fn_name}.fail")
257
            )
258
            resp["message"] = message.format(**_prepare_message_values())
1✔
259

260
    @staticmethod
1✔
261
    def _call_assertion_method(
1✔
262
        assert_item: AssertionEntry,
263
    ) -> ValueError | bool:
264
        """Call assertion method
265

266
        Args:
267
            assert_item: AssertionEntry
268
        Returns:
269
            ValueError | bool
270
        """
271

272
        asrt_fn = MAP_TYPE_TO_FN[assert_item.assert_type]
1✔
273
        return asrt_fn(**assert_item.as_dict)
1✔
274

275
    @staticmethod
1✔
276
    def test_run(
1✔
277
        assert_list: list[AssertionEntry], variables: dict
278
    ) -> AllTestRunResult:
279
        """Run the tests
280

281
        Args:
282
            assert_list: list[AssertionEntry]
283
            variables: dict
284

285
        Returns:
286
            AllTestRunResult: Test run result
287
        """
288

289
        test_run_result = AllTestRunResult(
1✔
290
            id=str(uuid.uuid4()),
291
            time_start=datetime.now(),
292
            count_all=len(assert_list),
293
            count_fail=0,
294
        )
295

296
        results: list[SingleTestRunResult] = []
1✔
297

298
        for assert_item in assert_list:
1✔
299
            assert_item = AssertionEntryListRunner._replace_assertion_values(
1✔
300
                assert_item, variables
301
            )
302

303
            resp = SingleTestRunResult(assert_used=assert_item)
1✔
304
            asrt_resp = AssertionEntryListRunner._call_assertion_method(assert_item)
1✔
305

306
            AssertionEntryListRunner._prepare_test_run_result(
1✔
307
                resp, assert_item, asrt_resp
308
            )
309

310
            if resp["is_pass"] is False:
1✔
311
                test_run_result["count_fail"] += 1
×
312

313
            results.append(resp)
1✔
314

315
        test_run_result["time_end"] = datetime.now()
1✔
316
        test_run_result["results"] = results
1✔
317

318
        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