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

chkware / cli / 5983435862

26 Aug 2023 07:00AM UTC coverage: 86.01% (+2.4%) from 83.627%
5983435862

push

github

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

Featute: String assertion funcitons for mod validate

80 of 80 new or added lines in 2 files covered. (100.0%)

1826 of 2123 relevant lines covered (86.01%)

0.86 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
}
46

47

48
@dataclasses.dataclass
1✔
49
class AssertionEntry:
1✔
50
    """AssertionEntry holds one assertion operation"""
51

52
    assert_type: str
1✔
53
    actual: typing.Any
1✔
54
    expected: typing.Any
1✔
55
    msg_pass: str = dataclasses.field(default_factory=str)
1✔
56
    msg_fail: str = dataclasses.field(default_factory=str)
1✔
57
    cast_actual_to: str = dataclasses.field(default_factory=str)
1✔
58
    actual_given: typing.Any = dataclasses.field(default=NotImplemented)
1✔
59
    actual_b4_cast: typing.Any = dataclasses.field(default=NotImplemented)
1✔
60
    extra_fields: dict = dataclasses.field(default_factory=dict)
1✔
61

62
    @property
1✔
63
    def as_dict(self) -> dict:
1✔
64
        """Return dict representation"""
65

66
        return dataclasses.asdict(self)
1✔
67

68

69
class SingleTestRunResult(UserDict):
1✔
70
    """Result of an assertion run
71

72
    keys: is_pass, message, assert_used
73
    """
74

75
    @property
1✔
76
    def as_dict(self) -> dict:
1✔
77
        """Convert SingleTestRunResult to a dict"""
78

79
        return {
1✔
80
            key: value.as_dict if key == "assert_used" else value
81
            for key, value in self.items()
82
        }
83

84
    @property
1✔
85
    def as_fmt_str(self) -> str:
1✔
86
        """String representation of ApiResponse
87

88
        Returns:
89
            str: String representation
90
        """
91

92
        return (
1✔
93
            "\n"
94
            f"{'+' if self['is_pass'] else '-'} {self['assert_used'].assert_type} "
95
            + f"{'PASSED' if self['is_pass'] else 'FAILED'}, {self['message']}"
96
        )
97

98

99
class AllTestRunResult(UserDict):
1✔
100
    """Result of a test run
101

102
    keys: id, time_start, time_end, count_all, results, count_fail
103
    """
104

105
    @property
1✔
106
    def as_dict(self) -> dict:
1✔
107
        """Convert AllTestRunResult to a dict"""
108
        _as_dict: dict = {
1✔
109
            key: value for key, value in self.items() if not key.startswith("time_")
110
        }
111

112
        _as_dict |= {
1✔
113
            key: value.timestamp()
114
            for key, value in self.items()
115
            if key.startswith("time_")
116
        }
117

118
        if len(self["results"]) > 0:
1✔
119
            _as_dict["results"] = [
1✔
120
                test_result.as_dict
121
                for test_result in self["results"]
122
                if isinstance(test_result, SingleTestRunResult)
123
            ]
124

125
        return _as_dict
1✔
126

127
    @property
1✔
128
    def as_fmt_str(self) -> str:
1✔
129
        """String representation of ApiResponse
130

131
        Returns:
132
            str: String representation
133
        """
134

135
        _display = (
1✔
136
            f"Test run id: {self['id']}, time taken {self['time_end'] - self['time_start']}\n"
137
            + f"Total tests: {self['count_all']}, "
138
            + f"Total tests failed: {self['count_fail']}\n"
139
        )
140
        _display += "\n> Test run result(s):"
1✔
141

142
        for one_result in self["results"]:
1✔
143
            _display += one_result.as_fmt_str
1✔
144

145
        return _display
1✔
146

147

148
class AssertionEntryListRunner:
1✔
149
    """AssertionAntiquary is service class that run assertion"""
150

151
    @staticmethod
1✔
152
    def _replace_assertion_values(
1✔
153
        assert_item: AssertionEntry, variable_d: dict
154
    ) -> AssertionEntry:
155
        """Replace value for actual and expected data
156

157
        Args:
158
            assert_item: AssertionEntry
159
            variable_d: dict
160
        Returns:
161
            AssertionEntry
162
        """
163

164
        # replace actual value for template
165
        if (
1✔
166
            isinstance(assert_item.actual, str)
167
            and "{{" in assert_item.actual
168
            and "}}" in assert_item.actual
169
        ):
170
            assert_item.actual_given = assert_item.actual
1✔
171
            assert_item.actual = linear_replace(assert_item.actual, variable_d)
1✔
172

173
        # convert actual value type
174
        if assert_item.cast_actual_to != "" and isinstance(assert_item.actual, str):
1✔
175
            assert_item.actual_b4_cast = assert_item.actual
×
176

177
            if assert_item.cast_actual_to == "int_or_flot":
×
178
                assert_item.actual = Cast.to_int_or_float(assert_item.actual)
×
179
            elif assert_item.cast_actual_to == "int":
×
180
                assert_item.actual = Cast.to_int(assert_item.actual)
×
181
            elif assert_item.cast_actual_to == "float":
×
182
                assert_item.actual = Cast.to_float(assert_item.actual)
×
183
            elif assert_item.cast_actual_to == "bool":
×
184
                assert_item.actual = Cast.to_bool(assert_item.actual)
×
185
            elif assert_item.cast_actual_to == "none":
×
186
                assert_item.actual = Cast.to_none(assert_item.actual)
×
187
            elif assert_item.cast_actual_to in ["dict", "list", "str"]:
×
188
                assert_item.actual = Cast.to_hashable(assert_item.actual)
×
189
            elif assert_item.cast_actual_to == "auto":
×
190
                assert_item.actual = Cast.to_auto(assert_item.actual)
×
191

192
        # replace expected value for template
193
        if (
1✔
194
            isinstance(assert_item.expected, str)
195
            and "{{" in assert_item.expected
196
            and "}}" in assert_item.expected
197
        ):
198
            assert_item.expected = linear_replace(assert_item.expected, variable_d)
1✔
199

200
        return assert_item
1✔
201

202
    @staticmethod
1✔
203
    def _prepare_test_run_result(
1✔
204
        resp: SingleTestRunResult,
205
        assert_item: AssertionEntry,
206
        asrt_resp: ValueError | bool,
207
    ) -> None:
208
        def _prepare_message_values() -> dict:
1✔
209
            return {
1✔
210
                "assert_type": assert_item.assert_type,
211
                "type_actual": assert_item.actual.__class__.__name__,
212
                "type_expected": assert_item.expected.__class__.__name__,
213
                "value_actual": assert_item.actual,
214
                "value_expected": assert_item.expected,
215
                "value_actual_given": assert_item.actual_given,
216
                "value_actual_b4_cast": assert_item.actual_b4_cast,
217
                "extra_fields": assert_item.extra_fields,
218
            }
219

220
        asrt_fn_name = MAP_TYPE_TO_FN[assert_item.assert_type].__name__
1✔
221

222
        if isinstance(asrt_resp, ValueError):
1✔
223
            resp["is_pass"] = False
×
224
            resp["message"] = get_assert_msg_for(
×
225
                f"{asrt_fn_name}.{str(asrt_resp)}"
226
            ).format(**_prepare_message_values())
227
        else:
228
            resp["is_pass"] = asrt_resp
1✔
229

230
            message = (
1✔
231
                get_assert_msg_for(f"{asrt_fn_name}.pass")
232
                if asrt_resp
233
                else get_assert_msg_for(f"{asrt_fn_name}.fail")
234
            )
235
            resp["message"] = message.format(**_prepare_message_values())
1✔
236

237
    @staticmethod
1✔
238
    def _call_assertion_method(
1✔
239
        assert_item: AssertionEntry,
240
    ) -> ValueError | bool:
241
        """Call assertion method
242

243
        Args:
244
            assert_item: AssertionEntry
245
        Returns:
246
            ValueError | bool
247
        """
248

249
        asrt_fn = MAP_TYPE_TO_FN[assert_item.assert_type]
1✔
250
        return asrt_fn(**assert_item.as_dict)
1✔
251

252
    @staticmethod
1✔
253
    def test_run(
1✔
254
        assert_list: list[AssertionEntry], variables: dict
255
    ) -> AllTestRunResult:
256
        """Run the tests
257

258
        Args:
259
            assert_list: list[AssertionEntry]
260
            variables: dict
261

262
        Returns:
263
            AllTestRunResult: Test run result
264
        """
265

266
        test_run_result = AllTestRunResult(
1✔
267
            id=str(uuid.uuid4()),
268
            time_start=datetime.now(),
269
            count_all=len(assert_list),
270
            count_fail=0,
271
        )
272

273
        results: list[SingleTestRunResult] = []
1✔
274

275
        for assert_item in assert_list:
1✔
276
            assert_item = AssertionEntryListRunner._replace_assertion_values(
1✔
277
                assert_item, variables
278
            )
279

280
            resp = SingleTestRunResult(assert_used=assert_item)
1✔
281
            asrt_resp = AssertionEntryListRunner._call_assertion_method(assert_item)
1✔
282

283
            AssertionEntryListRunner._prepare_test_run_result(
1✔
284
                resp, assert_item, asrt_resp
285
            )
286

287
            if resp["is_pass"] is False:
1✔
288
                test_run_result["count_fail"] += 1
×
289

290
            results.append(resp)
1✔
291

292
        test_run_result["time_end"] = datetime.now()
1✔
293
        test_run_result["results"] = results
1✔
294

295
        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