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

chkware / cli / 5989533698

27 Aug 2023 07:11AM UTC coverage: 86.895% (+0.4%) from 86.472%
5989533698

push

github

web-flow
Merge pull request #240 from 0hsn/feat/mod-validate-assertion-func-map

Feature: Dictionary assertion function for validate mod

74 of 74 new or added lines in 3 files covered. (100.0%)

2029 of 2335 relevant lines covered (86.9%)

0.87 hits per line

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

82.52
/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
        return dataclasses.asdict(self)
1✔
83

84

85
class SingleTestRunResult(UserDict):
1✔
86
    """Result of an assertion run
87

88
    keys: is_pass, message, assert_used
89
    """
90

91
    @property
1✔
92
    def as_dict(self) -> dict:
1✔
93
        """Convert SingleTestRunResult to a dict"""
94

95
        return {
1✔
96
            key: value.as_dict if key == "assert_used" else value
97
            for key, value in self.items()
98
        }
99

100
    @property
1✔
101
    def as_fmt_str(self) -> str:
1✔
102
        """String representation of ApiResponse
103

104
        Returns:
105
            str: String representation
106
        """
107

108
        return (
1✔
109
            "\n"
110
            f"{'+' if self['is_pass'] else '-'} {self['assert_used'].assert_type} "
111
            + f"{'PASSED' if self['is_pass'] else 'FAILED'}, {self['message']}"
112
        )
113

114

115
class AllTestRunResult(UserDict):
1✔
116
    """Result of a test run
117

118
    keys: id, time_start, time_end, count_all, results, count_fail
119
    """
120

121
    @property
1✔
122
    def as_dict(self) -> dict:
1✔
123
        """Convert AllTestRunResult to a dict"""
124
        _as_dict: dict = {
1✔
125
            key: value for key, value in self.items() if not key.startswith("time_")
126
        }
127

128
        _as_dict |= {
1✔
129
            key: value.timestamp()
130
            for key, value in self.items()
131
            if key.startswith("time_")
132
        }
133

134
        if len(self["results"]) > 0:
1✔
135
            _as_dict["results"] = [
1✔
136
                test_result.as_dict
137
                for test_result in self["results"]
138
                if isinstance(test_result, SingleTestRunResult)
139
            ]
140

141
        return _as_dict
1✔
142

143
    @property
1✔
144
    def as_fmt_str(self) -> str:
1✔
145
        """String representation of ApiResponse
146

147
        Returns:
148
            str: String representation
149
        """
150

151
        _display = (
1✔
152
            f"Test run id: {self['id']}, time taken {self['time_end'] - self['time_start']}\n"
153
            + f"Total tests: {self['count_all']}, "
154
            + f"Total tests failed: {self['count_fail']}\n"
155
        )
156
        _display += "\n> Test run result(s):"
1✔
157

158
        for one_result in self["results"]:
1✔
159
            _display += one_result.as_fmt_str
1✔
160

161
        return _display
1✔
162

163

164
class AssertionEntryListRunner:
1✔
165
    """AssertionAntiquary is service class that run assertion"""
166

167
    @staticmethod
1✔
168
    def _replace_assertion_values(
1✔
169
        assert_item: AssertionEntry, variable_d: dict
170
    ) -> AssertionEntry:
171
        """Replace value for actual and expected data
172

173
        Args:
174
            assert_item: AssertionEntry
175
            variable_d: dict
176
        Returns:
177
            AssertionEntry
178
        """
179

180
        # replace actual value for template
181
        if (
1✔
182
            isinstance(assert_item.actual, str)
183
            and "{{" in assert_item.actual
184
            and "}}" in assert_item.actual
185
        ):
186
            assert_item.actual_given = assert_item.actual
1✔
187
            assert_item.actual = linear_replace(assert_item.actual, variable_d)
1✔
188

189
        # convert actual value type
190
        if assert_item.cast_actual_to != "" and isinstance(assert_item.actual, str):
1✔
191
            assert_item.actual_b4_cast = assert_item.actual
×
192

193
            if assert_item.cast_actual_to == "int_or_flot":
×
194
                assert_item.actual = Cast.to_int_or_float(assert_item.actual)
×
195
            elif assert_item.cast_actual_to == "int":
×
196
                assert_item.actual = Cast.to_int(assert_item.actual)
×
197
            elif assert_item.cast_actual_to == "float":
×
198
                assert_item.actual = Cast.to_float(assert_item.actual)
×
199
            elif assert_item.cast_actual_to == "bool":
×
200
                assert_item.actual = Cast.to_bool(assert_item.actual)
×
201
            elif assert_item.cast_actual_to == "none":
×
202
                assert_item.actual = Cast.to_none(assert_item.actual)
×
203
            elif assert_item.cast_actual_to in ["map", "list", "str"]:
×
204
                assert_item.actual = Cast.to_hashable(assert_item.actual)
×
205
            elif assert_item.cast_actual_to == "auto":
×
206
                assert_item.actual = Cast.to_auto(assert_item.actual)
×
207

208
        # replace expected value for template
209
        if (
1✔
210
            isinstance(assert_item.expected, str)
211
            and "{{" in assert_item.expected
212
            and "}}" in assert_item.expected
213
        ):
214
            assert_item.expected = linear_replace(assert_item.expected, variable_d)
1✔
215

216
        return assert_item
1✔
217

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

236
        asrt_fn_name = MAP_TYPE_TO_FN[assert_item.assert_type].__name__
1✔
237

238
        if isinstance(asrt_resp, ValueError):
1✔
239
            resp["is_pass"] = False
×
240
            resp["message"] = get_assert_msg_for(
×
241
                f"{asrt_fn_name}.{str(asrt_resp)}"
242
            ).format(**_prepare_message_values())
243
        else:
244
            resp["is_pass"] = asrt_resp
1✔
245

246
            message = (
1✔
247
                get_assert_msg_for(f"{asrt_fn_name}.pass")
248
                if asrt_resp
249
                else get_assert_msg_for(f"{asrt_fn_name}.fail")
250
            )
251
            resp["message"] = message.format(**_prepare_message_values())
1✔
252

253
    @staticmethod
1✔
254
    def _call_assertion_method(
1✔
255
        assert_item: AssertionEntry,
256
    ) -> ValueError | bool:
257
        """Call assertion method
258

259
        Args:
260
            assert_item: AssertionEntry
261
        Returns:
262
            ValueError | bool
263
        """
264

265
        asrt_fn = MAP_TYPE_TO_FN[assert_item.assert_type]
1✔
266
        return asrt_fn(**assert_item.as_dict)
1✔
267

268
    @staticmethod
1✔
269
    def test_run(
1✔
270
        assert_list: list[AssertionEntry], variables: dict
271
    ) -> AllTestRunResult:
272
        """Run the tests
273

274
        Args:
275
            assert_list: list[AssertionEntry]
276
            variables: dict
277

278
        Returns:
279
            AllTestRunResult: Test run result
280
        """
281

282
        test_run_result = AllTestRunResult(
1✔
283
            id=str(uuid.uuid4()),
284
            time_start=datetime.now(),
285
            count_all=len(assert_list),
286
            count_fail=0,
287
        )
288

289
        results: list[SingleTestRunResult] = []
1✔
290

291
        for assert_item in assert_list:
1✔
292
            assert_item = AssertionEntryListRunner._replace_assertion_values(
1✔
293
                assert_item, variables
294
            )
295

296
            resp = SingleTestRunResult(assert_used=assert_item)
1✔
297
            asrt_resp = AssertionEntryListRunner._call_assertion_method(assert_item)
1✔
298

299
            AssertionEntryListRunner._prepare_test_run_result(
1✔
300
                resp, assert_item, asrt_resp
301
            )
302

303
            if resp["is_pass"] is False:
1✔
304
                test_run_result["count_fail"] += 1
×
305

306
            results.append(resp)
1✔
307

308
        test_run_result["time_end"] = datetime.now()
1✔
309
        test_run_result["results"] = results
1✔
310

311
        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