File: test_logger.py

package info (click to toggle)
anta 1.7.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,048 kB
  • sloc: python: 48,164; sh: 28; javascript: 9; makefile: 4
file content (158 lines) | stat: -rw-r--r-- 5,005 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
# Copyright (c) 2023-2025 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
"""Tests for anta.logger."""

from __future__ import annotations

import logging
from pathlib import Path
from unittest.mock import patch

import pytest

from anta.logger import Log, LogLevel, _get_file_handler, _get_rich_handler, anta_log_exception, exc_to_str, setup_logging, tb_to_str


@pytest.mark.parametrize(
    ("level", "path", "debug_value"),
    [
        pytest.param(Log.INFO, None, False, id="INFO no file"),
        pytest.param(Log.DEBUG, None, False, id="DEBUG no file"),
        pytest.param(Log.INFO, Path("/tmp/file.log"), False, id="INFO file"),
        pytest.param(Log.DEBUG, Path("/tmp/file.log"), False, id="DEBUG file"),
        pytest.param(Log.INFO, None, True, id="INFO no file __DEBUG__ set"),
        pytest.param(Log.DEBUG, None, True, id="INFO no file __DEBUG__ set"),
    ],
)
def test_setup_logging(level: LogLevel, path: Path | None, debug_value: bool) -> None:
    """Test setup_logging."""
    # Clean up any logger on root
    root = logging.getLogger()
    if root.hasHandlers():
        root.handlers = []

    with patch("anta.logger.__DEBUG__", new=debug_value):
        setup_logging(level, path)

    rich_handler = _get_rich_handler(root)
    assert rich_handler is not None

    # When __DEBUG__ is True, the log level is overwritten to DEBUG
    if debug_value:
        assert root.level == logging.DEBUG
        if path is not None:
            assert rich_handler.level == logging.INFO

    if path is not None:
        assert _get_file_handler(root, path) is not None
        expected_handlers = 2
    else:
        expected_handlers = 1
    assert len(root.handlers) == expected_handlers

    # Check idempotency
    setup_logging(level, path)
    assert len(root.handlers) == expected_handlers


@pytest.mark.parametrize(
    ("exception", "message", "calling_logger", "debug_value", "expected_message"),
    [
        pytest.param(
            ValueError("exception message"),
            None,
            None,
            False,
            "ValueError: exception message",
            id="exception only",
        ),
        pytest.param(
            ValueError("exception message"),
            "custom message",
            None,
            False,
            "custom message\nValueError: exception message",
            id="custom message",
        ),
        pytest.param(
            ValueError("exception message"),
            "custom logger",
            logging.getLogger("custom"),
            False,
            "custom logger\nValueError: exception message",
            id="custom logger",
        ),
        pytest.param(
            ValueError("exception message"),
            "Use with custom message",
            None,
            True,
            "Use with custom message\nValueError: exception message",
            id="__DEBUG__ on",
        ),
    ],
)
def test_anta_log_exception(
    caplog: pytest.LogCaptureFixture,
    exception: Exception,
    message: str | None,
    calling_logger: logging.Logger | None,
    debug_value: bool,
    expected_message: str,
) -> None:
    """Test anta_log_exception."""
    if calling_logger is not None:
        # https://github.com/pytest-dev/pytest/issues/3697
        calling_logger.propagate = True
        caplog.set_level(logging.ERROR, logger=calling_logger.name)
    else:
        caplog.set_level(logging.ERROR)
    # Need to raise to trigger nice stacktrace for __DEBUG__ == True
    try:
        raise exception
    except ValueError as exc:
        with patch("anta.logger.__DEBUG__", new=debug_value):
            anta_log_exception(exc, message=message, calling_logger=calling_logger)

    # Two log captured
    if debug_value:
        assert len(caplog.record_tuples) == 2
    else:
        assert len(caplog.record_tuples) == 1
    logger, level, message = caplog.record_tuples[0]

    if calling_logger is not None:
        assert calling_logger.name == logger
    else:
        assert logger == "anta.logger"

    assert level == logging.CRITICAL
    assert message == expected_message
    # the only place where we can see the stracktrace is in the capture.text
    if debug_value:
        assert "Traceback" in caplog.text


def my_raising_function(exception: Exception) -> None:
    """Raise Exception."""
    raise exception


@pytest.mark.parametrize(
    ("exception", "expected_output"),
    [(ValueError("test"), "ValueError: test"), (ValueError(), "ValueError")],
)
def test_exc_to_str(exception: Exception, expected_output: str) -> None:
    """Test exc_to_str."""
    assert exc_to_str(exception) == expected_output


def test_tb_to_str() -> None:
    """Test tb_to_str."""
    try:
        my_raising_function(ValueError("test"))
    except ValueError as exc:
        output = tb_to_str(exc)
        assert "Traceback" in output
        assert 'my_raising_function(ValueError("test"))' in output