File: test_asfixture.py

package info (click to toggle)
python-logassert 7-1.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 160 kB
  • sloc: python: 641; makefile: 3; sh: 1
file content (457 lines) | stat: -rw-r--r-- 12,649 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# Copyright 2020-2022 Facundo Batista
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser  General Public License version 3, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# For further info, check  https://github.com/facundobatista/logassert

"""Tests for the main module when used as a pytest fixture."""

import logging

import pytest

from logassert import Exact, Multiple, Sequence, NOTHING

logger = logging.getLogger()


# -- Basic usage

def test_basic_simple_assert_ok_simple(logs):
    logger.debug("test")
    assert "test" in logs.any_level


def test_basic_simple_assert_ok_with_replaces(logs):
    logger.debug("test %d %r", 65, 'foobar')
    assert "test 65 'foobar'" in logs.any_level


def test_basic_simple_negated_assert_(logs):
    logger.debug("aaa")
    assert "aaa" not in logs.warning  # different than logged
    assert "bbb" not in logs.any_level  # checking other text in any level
    assert "bbb" not in logs.debug  # checking other text in same level


def test_reset(logs):
    logger.debug("foobar")
    assert "foobar" in logs.debug
    logs.reset()
    assert "foobar" not in logs.debug


# -- messages

def test_failure_message_simple(logs):
    logger.debug("aaa")
    with pytest.raises(AssertionError) as err:
        assert "bbb" in logs.debug
    assert str(err.value) == (
        "assert for regex 'bbb' check in DEBUG failed; logged lines:\n"
        "       DEBUG     'aaa'"
    )


def test_failure_message_no_logs(logs):
    with pytest.raises(AssertionError) as err:
        assert "bbb" in logs.debug
    assert str(err.value) == (
        "assert for regex 'bbb' check in DEBUG failed; no logged lines at all!"
    )


def test_failure_message_any_level(logs):
    logger.debug("aaa")
    with pytest.raises(AssertionError) as err:
        assert "bbb" in logs.any_level
    assert str(err.value) == (
        "assert for regex 'bbb' check in any level failed; logged lines:\n"
        "       DEBUG     'aaa'"
    )


def test_failure_message_multiple(logs):
    logger.debug("aaa")
    logger.warning("bbb")
    with pytest.raises(AssertionError) as err:
        assert "bbb" in logs.debug
    assert str(err.value) == (
        "assert for regex 'bbb' check in DEBUG failed; logged lines:\n"
        "       DEBUG     'aaa'\n"
        "       WARNING   'bbb'"
    )


def test_failure_message_exception(logs):
    try:
        raise ValueError("test error")
    except ValueError:
        logger.exception("test message")

    with pytest.raises(AssertionError) as err:
        assert "will make it fail" in logs.error
    assert str(err.value) == (
        "assert for regex 'will make it fail' check in ERROR failed; logged lines:\n"
        "       ERROR     'test message'"
    )


# -- different forms of comparison

def test_regex_matching_exact(logs):
    logger.debug("foo 42")
    assert r"foo 42" in logs.debug
    assert r"foo \d\d" in logs.debug


def test_regex_matching_inside(logs):
    logger.debug("foo bar 42")
    assert r"bar \d\d" in logs.debug
    assert r"ba." in logs.debug
    assert r"f.. bar" in logs.debug
    with pytest.raises(AssertionError):
        assert r"foo 42" in logs.debug


def test_regex_matching_forcing_complete(logs):
    logger.debug("foo x bar")
    assert r"^foo . bar$" in logs.debug
    with pytest.raises(AssertionError):
        assert r"^foo .$" in logs.debug


def test_exact_cases(logs):
    logger.debug("foo 42")
    assert Exact("foo 42") in logs.debug
    assert Exact("foo ..") not in logs.debug
    assert Exact("foo") not in logs.debug


def test_exact_failure(logs):
    logger.debug("aaa")
    comparer = logs.debug
    check_ok = comparer.__contains__(Exact("bbb"))
    assert not check_ok
    assert comparer.messages == [
        "for exact 'bbb' check in DEBUG failed; logged lines:",
        "     DEBUG     'aaa'",
    ]


def test_multiple_cases(logs):
    logger.debug("foo bar 42")
    assert Multiple("foo bar 42") in logs.debug
    assert Multiple("foo bar 42", "extra") not in logs.debug
    assert Multiple("foo", "bar") in logs.debug
    assert Multiple("42", "bar") in logs.debug
    assert Multiple("foo 42") not in logs.debug
    assert Multiple("foo.*") not in logs.debug


def test_multiple_failure(logs):
    logger.debug("aaa")
    comparer = logs.debug
    check_ok = comparer.__contains__(Multiple("bbb", 'ccc'))
    assert not check_ok
    assert comparer.messages == [
        "for multiple ('bbb', 'ccc') check in DEBUG failed; logged lines:",
        "     DEBUG     'aaa'",
    ]


def test_sequence_simple(logs):
    logger.debug("foo")
    logger.debug("a1")
    logger.debug("a2")
    logger.debug("bar")
    assert Sequence("a1", "a2") in logs.debug


def test_sequence_rotated(logs):
    logger.debug("a2")
    logger.debug("a1")
    comparer = logs.debug
    check_ok = comparer.__contains__(Sequence("a1", "a2"))
    assert not check_ok
    assert comparer.messages == [
        "for sequence ('a1', 'a2') check in DEBUG failed; logged lines:",
        "     DEBUG     'a2'",
        "     DEBUG     'a1'",
    ]


def test_sequence_partial(logs):
    logger.debug("a1")
    comparer = logs.debug
    check_ok = comparer.__contains__(Sequence("a1", "a2"))
    assert not check_ok
    assert comparer.messages == [
        "for sequence ('a1', 'a2') check in DEBUG failed; logged lines:",
        "     DEBUG     'a1'",
    ]


def test_sequence_interrupted(logs):
    logger.debug("a1")
    logger.debug("--")
    logger.debug("a2")
    comparer = logs.debug
    check_ok = comparer.__contains__(Sequence("a1", "a2"))
    assert not check_ok
    assert comparer.messages == [
        "for sequence ('a1', 'a2') check in DEBUG failed; logged lines:",
        "     DEBUG     'a1'",
        "     DEBUG     '--'",
        "     DEBUG     'a2'",
    ]


def test_sequence_inners(logs):
    logger.debug("foo")
    logger.debug("xxx a1")
    logger.debug("xxx a2")
    logger.debug("xxx a3")
    logger.debug("bar")
    assert Sequence(
        ".* a.",
        Exact("xxx a2"),
        Multiple("a3", "xxx"),
    ) in logs.debug


def test_basic_avoid_delayed_messaging(logs):
    class Exploding:
        """Explode on delayed str."""
        should_explode = False

        def __str__(self):
            if self.should_explode:
                raise ValueError("str exploded")
            return "didn't explode"

    # log something using the Exploding class
    exploding = Exploding()
    logger = logging.getLogger()
    logger.debug("feeling lucky? %s", exploding)

    # now flag the class to explode and check
    exploding.should_explode = True
    assert r"feeling lucky\? didn't explode" in logs.debug


def test_nothing_ok(logs):
    logger.debug("aaa")
    assert NOTHING in logs.warning


def test_nothing_fail_level(logs):
    logger.debug("aaa")
    with pytest.raises(AssertionError) as err:
        assert NOTHING in logs.debug
    assert str(err.value) == (
        "assert for nothing in DEBUG failed; logged lines:\n"
        "       DEBUG     'aaa'"
    )


def test_nothing_fail_any(logs):
    logger.debug("aaa")
    with pytest.raises(AssertionError) as err:
        assert NOTHING in logs.any_level
    assert str(err.value) == (
        "assert for nothing in any level failed; logged lines:\n"
        "       DEBUG     'aaa'"
    )


# -- Levels

def test_levels_assert_ok_debug(logs):
    logger.debug("test")
    assert "test" in logs.debug


def test_levels_assert_ok_info(logs):
    logger.info("test")
    assert "test" in logs.info


def test_levels_assert_ok_warning(logs):
    logger.warning("test")
    assert "test" in logs.warning


def test_levels_assert_ok_error(logs):
    logger.error("test")
    assert "test" in logs.error


def test_levels_assert_ok_exception(logs):
    try:
        raise ValueError("test error")
    except ValueError:
        logger.exception("test message")
    assert "^test message$" in logs.error


def test_levels_assert_different_level_fail_debug_warning(logs):
    logger.warning("test")
    with pytest.raises(AssertionError):
        assert "test" in logs.debug


def test_levels_assert_different_level_fail_warning_debug(logs):
    logger.debug("test")
    with pytest.raises(AssertionError):
        assert "test" in logs.warning


# -- Usage as a fixture!

def test_as_fixture_basic(testdir, pytestconfig):
    """Make sure that our plugin works."""
    # create a temporary conftest.py file
    plugin_fpath = pytestconfig.rootdir / 'logassert' / 'pytest_plugin.py'
    with plugin_fpath.open('rt', encoding='utf8') as fh:
        testdir.makeconftest(fh.read())

    # create a temporary pytest test file
    testdir.makepyfile(
        """
        def test_hello_default(logs):
            assert "anything" not in logs.any_level
    """
    )

    # run the test with pytest
    result = testdir.runpytest()

    # check that the test passed
    result.assert_outcomes(passed=1)


def test_as_fixture_double_handler(testdir, pytestconfig):
    """Check that we don't hook many handlers."""
    # create a temporary conftest.py file
    plugin_fpath = pytestconfig.rootdir / 'logassert' / 'pytest_plugin.py'
    with plugin_fpath.open('rt', encoding='utf8') as fh:
        testdir.makeconftest(fh.read())

    # create a temporary pytest test file
    testdir.makepyfile(
        """
        from logassert import logassert

        import logging

        logger = logging.getLogger()

        def test_1(logs):
            logger.debug('test')
            assert "test" in logs.any_level

        def test_2(logs):
            logger.debug('test')
            assert "test" in logs.debug
            assert len(
                [h for h in logger.handlers if isinstance(h, logassert._StoringHandler)]) == 1
    """
    )

    # run the test with pytest
    result = testdir.runpytest()
    print('\n'.join(result.stdout.lines))

    # check that the test passed
    result.assert_outcomes(passed=2)


def test_as_fixture_clean_up(testdir, pytestconfig):
    """Don't leave traces of setup."""
    # create a temporary conftest.py file
    plugin_fpath = pytestconfig.rootdir / 'logassert' / 'pytest_plugin.py'
    with plugin_fpath.open('rt', encoding='utf8') as fh:
        testdir.makeconftest(fh.read())

    # create a temporary pytest test file
    testdir.makepyfile(
        """
        import logging

        import pytest

        from logassert import logassert

        logger = logging.getLogger('')
        logger.setLevel(30)

        def test_1(logs):
            logger.debug('test')
            assert "test" in logs.any_level

        @pytest.fixture(scope="session", autouse=True)
        def cleanup(request):
            assert logger.getEffectiveLevel() == 30
            assert len(
                [h for h in logger.handlers if isinstance(h, logassert._StoringHandler)]) == 0
    """
    )

    # run the test with pytest
    result = testdir.runpytest()
    print('\n'.join(result.stdout.lines))

    # check that the test passed
    result.assert_outcomes(passed=1)


def test_as_fixture_no_record_leaking(testdir, pytestconfig):
    """Nothing is leaked between tests."""
    # create a temporary conftest.py file
    plugin_fpath = pytestconfig.rootdir / 'logassert' / 'pytest_plugin.py'
    with plugin_fpath.open('rt', encoding='utf8') as fh:
        testdir.makeconftest(fh.read())

    # create a temporary pytest test file
    testdir.makepyfile(
        """
        import logging

        logger = logging.getLogger()

        def test_1(logs):
            logger.debug('test')
            assert "test" in logs.any_level

        def test_2(logs):
            assert "test" not in logs.any_level
    """
    )

    # run the test with pytest
    result = testdir.runpytest()

    # check that the test passed
    result.assert_outcomes(passed=2)


def test_logged_lines_are_shown_when_using_not_in(logs):
    logger.error("foo")
    with pytest.raises(AssertionError) as err:
        assert "foo" not in logs.error

    expected_log = ("assert for regex 'foo' check in ERROR failed; logged lines:\n"
                    "       ERROR     'foo'")

    assert expected_log == str(err.value)