Skip to content

Commit 2354a99

Browse files
committed
Update tests for logguard package initialization and logger functionality
1 parent 9bb1235 commit 2354a99

6 files changed

Lines changed: 543 additions & 1592 deletions

File tree

tests/conftest.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# tests/conftest.py
2+
import logging
3+
from collections.abc import Generator
4+
from pathlib import Path
5+
from typing import Any
6+
7+
import pytest
8+
9+
from logguard.logger import AppLogger
10+
11+
12+
@pytest.fixture(autouse=True)
13+
def reset_logger() -> Generator[None, None, None]:
14+
"""Limpia la configuración del logger antes y después de cada test"""
15+
AppLogger.reset()
16+
yield
17+
AppLogger.reset()
18+
19+
20+
@pytest.fixture
21+
def temp_log_dir(tmp_path: Path) -> Generator[Path, None, None]:
22+
"""Directorio temporal para logs"""
23+
log_dir: Path = tmp_path / "logs"
24+
log_dir.mkdir()
25+
yield log_dir
26+
# cleanup opcional
27+
28+
29+
@pytest.fixture
30+
def capture_logs(caplog: pytest.LogCaptureFixture) -> pytest.LogCaptureFixture:
31+
"""Usa caplog de pytest para capturar logs"""
32+
caplog.set_level(logging.DEBUG, logger="logguard")
33+
caplog.set_level(logging.DEBUG, logger="assertions")
34+
return caplog
35+
36+
37+
@pytest.fixture
38+
def no_rich(monkeypatch: pytest.MonkeyPatch) -> None:
39+
"""Simula que rich no está instalado"""
40+
monkeypatch.setattr("logguard.logger.RICH_AVAILABLE", False)
41+
monkeypatch.setattr("logguard.logger.RichHandler", None)
42+
monkeypatch.setattr("logguard.logger.Console", None)
43+
44+
45+
@pytest.fixture
46+
def no_json_logger(monkeypatch: pytest.MonkeyPatch) -> None:
47+
"""Simula que python-json-logger no está instalado"""
48+
49+
original_import = __import__
50+
51+
def mock_import(name: str, *args: Any, **kwargs: Any) -> Any:
52+
if name.startswith("pythonjsonlogger"):
53+
raise ImportError(f"No module named '{name}'")
54+
return original_import(name, *args, **kwargs)
55+
56+
monkeypatch.setattr("builtins.__import__", mock_import)

tests/test_assertions.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import pytest
2+
3+
from logguard.asserts import ASSERT, ASSERT_IN, ASSERT_RANGE, ASSERT_TYPE, enforce, set_failure_handler
4+
from logguard.exceptions import ValidationError
5+
6+
7+
def test_enforce_success() -> None:
8+
enforce(True, "esto no debería fallar")
9+
# no exception
10+
11+
12+
def test_enforce_fail(capture_logs: pytest.LogCaptureFixture) -> None:
13+
with pytest.raises(ValidationError) as exc_info:
14+
enforce(False, "condición fallida", extra={"valor": 42})
15+
16+
assert "condición fallida" in str(exc_info.value)
17+
assert "valor=42" in capture_logs.text
18+
19+
20+
def test_ASSERT_simple(capture_logs: pytest.LogCaptureFixture) -> None:
21+
x = 10
22+
ASSERT(x > 5)
23+
# no raise
24+
25+
with pytest.raises(ValidationError):
26+
ASSERT(x < 0, "x debería ser negativo")
27+
28+
29+
def test_ASSERT_with_message_and_extra(capture_logs: pytest.LogCaptureFixture) -> None:
30+
extra_data = {"test_id": 777}
31+
with pytest.raises(ValidationError) as exc:
32+
ASSERT(False, "prueba fallida", extra=extra_data)
33+
34+
assert "prueba fallida" in str(exc.value)
35+
# Extra is in the logs
36+
assert "test_id=777" in capture_logs.text
37+
38+
39+
def test_custom_failure_handler() -> None:
40+
from typing import Any
41+
42+
calls: list[tuple[str, str, dict[str, Any]]] = []
43+
44+
def custom_handler(msg: str, expr: str, fn: str, ln: int, func: str, extra: dict[str, Any]) -> None:
45+
calls.append((msg, expr, extra))
46+
raise ValidationError(msg)
47+
48+
set_failure_handler(custom_handler)
49+
50+
with pytest.raises(ValidationError):
51+
ASSERT(False, "custom test")
52+
53+
assert len(calls) == 1
54+
assert "custom test" in calls[0][0]
55+
56+
57+
def test_ASSERT_type_helpers() -> None:
58+
ASSERT_TYPE(42, int)
59+
60+
with pytest.raises(ValidationError):
61+
ASSERT_TYPE("42", int)
62+
63+
ASSERT_IN("a", ["a", "b", "c"])
64+
65+
with pytest.raises(ValidationError):
66+
ASSERT_IN("z", ["a", "b", "c"])
67+
68+
69+
@pytest.mark.parametrize(
70+
"value, minv, maxv, should_pass",
71+
[
72+
(50, 0, 100, True),
73+
(-1, 0, 100, False),
74+
(100, 0, 100, True),
75+
(101, 0, 100, False),
76+
],
77+
)
78+
def test_ASSERT_RANGE(value: int, minv: int, maxv: int, should_pass: bool) -> None:
79+
if should_pass:
80+
ASSERT_RANGE(value, minv, maxv)
81+
else:
82+
with pytest.raises(ValidationError):
83+
ASSERT_RANGE(value, minv, maxv)
84+
85+
86+
def test_no_raise_in_non_debug() -> None:
87+
# En modo debug, siempre lanza excepción
88+
# Este test verifica que __debug__ esté habilitado en testing
89+
assert __debug__ is True # True by default in tests
90+
91+
92+
@pytest.mark.parametrize(
93+
"assert_func, args, kwargs, error_pattern",
94+
[
95+
(ASSERT_TYPE, ("not an int", int), {}, "type"),
96+
(ASSERT_IN, ("z", ["a", "b", "c"]), {}, ("not found", "not in", "container")),
97+
],
98+
)
99+
def test_assert_functions_fail(assert_func, args, kwargs, error_pattern) -> None:
100+
"""Test ASSERT_TYPE and ASSERT_IN with failed assertions."""
101+
with pytest.raises(ValidationError) as exc:
102+
assert_func(*args, **kwargs)
103+
104+
error_str = str(exc.value).lower()
105+
106+
# Handle both single string and tuple of possible patterns
107+
if isinstance(error_pattern, tuple):
108+
assert any(pattern in error_str for pattern in error_pattern)
109+
else:
110+
assert error_pattern in error_str
111+
112+
113+
def test_enforce_with_expression() -> None:
114+
"""Test enforce with explicit expression."""
115+
with pytest.raises(ValidationError) as exc:
116+
enforce(
117+
False,
118+
"test message",
119+
expression="x > 0",
120+
filename="test.py",
121+
line=42,
122+
function="test_func",
123+
)
124+
error_dict = exc.value.to_dict()
125+
assert error_dict["type"] == "ValidationError"
126+
assert "test message" in error_dict["message"]
127+
128+
129+
def test_reset_failure_handler() -> None:
130+
"""Test resetting failure handler to default."""
131+
from typing import Any
132+
133+
from logguard.asserts import _default_failure_handler, set_failure_handler
134+
135+
custom_called: list[bool] = [False]
136+
137+
def custom(msg: str, expr: str, fn: str, ln: int, func: str, extra: dict[str, Any]) -> None:
138+
custom_called[0] = True
139+
raise ValidationError(msg)
140+
141+
set_failure_handler(custom)
142+
# Reset to default
143+
set_failure_handler(_default_failure_handler)
144+
145+
with pytest.raises(ValidationError):
146+
ASSERT(False, "test")
147+
148+
149+
def test_ASSERT_without_message() -> None:
150+
"""Test ASSERT without explicit message."""
151+
with pytest.raises(ValidationError):
152+
ASSERT(False) # No message provided

0 commit comments

Comments
 (0)