test(logging); add tests for setup_handler and setup_formatter function

This commit is contained in:
2026-07-29 19:42:52 +02:00
parent d02887f164
commit 63893513ee
7 changed files with 218 additions and 11 deletions
+1
View File
@@ -3,3 +3,4 @@ output/
src/logwatcher/__pycache__/ src/logwatcher/__pycache__/
*.log *.log
__pycache__
+7
View File
@@ -0,0 +1,7 @@
{
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
+1
View File
@@ -6,6 +6,7 @@ readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [
"mypy>=2.3.0", "mypy>=2.3.0",
"pytest>=9.1.1",
] ]
[project.scripts] [project.scripts]
+12 -11
View File
@@ -2,14 +2,14 @@ import logging
from pathlib import Path from pathlib import Path
def setup_formatter( def _setup_formatter(
format: str = '[%(asctime)s] - %(levelname)s: %(message)s', format: str = '[%(asctime)s] - %(levelname)s: %(message)s',
datefmt: str = '%d/%m/%Y %H:%M:%S' datefmt: str = '%d/%m/%Y %H:%M:%S'
) -> logging.Formatter: ) -> logging.Formatter:
return logging.Formatter(fmt=format, datefmt=datefmt) return logging.Formatter(fmt=format, datefmt=datefmt)
def setup_file_handler(formatter: logging.Formatter, level: int , filename: Path | None = None) -> logging.Handler: def _setup_handler(formatter: logging.Formatter, level: int , filename: Path | None = None) -> logging.Handler:
handler: logging.Handler handler: logging.Handler
if filename: if filename:
handler = logging.FileHandler(filename=filename, encoding='utf-8') handler = logging.FileHandler(filename=filename, encoding='utf-8')
@@ -20,23 +20,24 @@ def setup_file_handler(formatter: logging.Formatter, level: int , filename: Path
return handler return handler
def setup_logging() -> logging.Logger: def setup_logging(formatter: logging.Formatter|None = None, file_path: Path = Path('output/logs/logwatcher.log')) -> logging.Logger:
""" """
Create a new instance of Logger named 'logger' customized for the logwatcher package. Create a new instance of Logger named 'logger' customized for the logwatcher package.
The format display in order the time, the level of log and the log message The format display in order the time, the level of log and the log message
The logs are only redirect in the standard output. The logs are only redirect in the standard output.
""" """
logger = logging.getLogger("logwatcher_log") _formatter = formatter if formatter else _setup_formatter()
formatter = setup_formatter() standard_output = _setup_handler(_formatter, logging.ERROR)
file_output = _setup_handler(_formatter, logging.DEBUG, file_path)
standard_output = setup_file_handler(formatter, logging.ERROR) root = logging.getLogger()
root.setLevel(logging.DEBUG)
file_path = Path('output/logs/logwatcher.log') root.addHandler(standard_output)
file_output = setup_file_handler(formatter, logging.DEBUG, file_path) root.addHandler(file_output)
logger = logging.getLogger("logwatcher")
logger.addHandler(standard_output) logger.setLevel(logging.NOTSET)
logger.addHandler(file_output)
return logger return logger
+19
View File
@@ -0,0 +1,19 @@
# tests/conftest.py
import os
from pathlib import Path
import pytest
from logwatcher.logging import setup_logging
# Forcer un dossier temporaire pour les tests
os.environ["PYTEST_DEBUG_TEMPROOT"] = str(Path("output/test").absolute())
@pytest.fixture
def tmp_log_file(tmp_path):
return tmp_path / "logwatcher_test.log"
@pytest.fixture(autouse=True)
def tmp_setup_logging(tmp_log_file):
"""Redirige les logs vers un dossier temporaire pendant les tests."""
setup_logging(file_path=tmp_log_file)
+101
View File
@@ -0,0 +1,101 @@
"""
this module contains all tests logging-related :
- the logger should have at least one StreamHandler and one FileHandler each time
logwatcher is launched.
- the StreamHandler must watch at an error level
- the FileHandler must watch at a debug
level
-
"""
import re
from logging import DEBUG, ERROR, INFO, WARNING, FileHandler, StreamHandler, getLogger
import pytest
from logwatcher import logger
from logwatcher.logging import _setup_formatter, _setup_handler, setup_logging
def test_setup_handler(tmp_log_file):
formatter = _setup_formatter()
handler = _setup_handler(formatter=formatter, level=DEBUG)
assert isinstance(handler, StreamHandler)
handler = _setup_handler(formatter=formatter, level=INFO,filename=tmp_log_file)
assert isinstance(handler, FileHandler)
def test_count_of_handler(tmp_log_file):
logger = getLogger()
count_file_handler = 0
count_stream_handler = 0
for handler in logger.handlers:
if isinstance(handler, FileHandler):
count_file_handler += 1
elif isinstance(handler, StreamHandler):
count_stream_handler += 1
else:
pass
assert count_file_handler >= 1
assert count_stream_handler >= 1
def test_type_of_handlers():
for handler in logger.handlers:
if isinstance(handler, FileHandler):
assert handler.level == DEBUG
elif isinstance(handler, StreamHandler):
assert handler.level == ERROR
else:
pytest.fail("handlers must be either a file or a stream handler")
assert handler.formatter._fmt == '[%(asctime)s] - %(levelname)s: %(message)s'
assert handler.formatter.datefmt == '%d/%m/%Y %H:%M:%S'
def test_modified_format():
"""
when starting, the logger is first intialized, thus two handlers are at position
1 and 2 in logger.handlers list.
"""
logger = setup_logging()
for handler in logger.handlers[2:]:
assert handler.formatter._fmt == '[%(asctime)s] - %(levelname)s: %(message)s'
formatter_different_format = _setup_formatter(format='%(asctime)s - %(levelname)s: %(message)s')
logger = setup_logging(formatter=formatter_different_format)
for handler in logger.handlers[4:]:
assert handler.formatter._fmt == '%(asctime)s - %(levelname)s: %(message)s'
formatter_different_datefmt = _setup_formatter(datefmt='%a %b %Y at %Hh%M')
logger = setup_logging(formatter=formatter_different_datefmt)
for handler in logger.handlers[6:]:
assert handler.formatter._fmt == '[%(asctime)s] - %(levelname)s: %(message)s'
assert handler.formatter.datefmt == '%a %b %Y at %Hh%M'
def test_logging_in_correct_path(tmp_log_file):
# must be both in file and stdout
logger.critical("hi- BYE")
logger.error("hi... ok, bye")
logger.warning("hi, are you alright?")
# only in file
logger.info("hi there")
logger.debug("hi, how are you? it's been a while!")
with open(tmp_log_file) as log_file:
content = log_file.readlines()
regex = r'\[\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}\] - '
assert len(content) == 5
for line in content:
assert re.search(regex, line)
# test critical to warning
assert re.search(r'CRITICAL: hi- BYE', content[0])
assert re.search(r'ERROR: hi... ok, bye', content[1])
assert re.search(r'WARNING: hi, are you alright?', content[2])
assert re.search(r'INFO: hi there', content[3])
assert logger.getEffectiveLevel() < WARNING
assert re.search(r"DEBUG: hi, how are you\? it's been a while!", content[4])
Generated
+77
View File
@@ -47,6 +47,36 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/66/40/c53deb2cd0c9b0fb636d24d9f40924cf2e65028e6b20b10cd5c1eeb2c730/ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e", size = 1072965, upload-time = "2026-06-30T20:02:54.097Z" }, { url = "https://files.pythonhosted.org/packages/66/40/c53deb2cd0c9b0fb636d24d9f40924cf2e65028e6b20b10cd5c1eeb2c730/ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e", size = 1072965, upload-time = "2026-06-30T20:02:54.097Z" },
] ]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
]
[[package]]
name = "iniconfig"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
]
[[package]] [[package]]
name = "librt" name = "librt"
version = "0.13.0" version = "0.13.0"
@@ -140,6 +170,7 @@ version = "0.1.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "mypy" }, { name = "mypy" },
{ name = "pytest" },
] ]
[package.optional-dependencies] [package.optional-dependencies]
@@ -152,6 +183,7 @@ dev = [
requires-dist = [ requires-dist = [
{ name = "mypy", specifier = ">=2.3.0" }, { name = "mypy", specifier = ">=2.3.0" },
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=2.3.0" }, { name = "mypy", marker = "extra == 'dev'", specifier = ">=2.3.0" },
{ name = "pytest", specifier = ">=9.1.1" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.16.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.16.0" },
] ]
provides-extras = ["dev"] provides-extras = ["dev"]
@@ -225,6 +257,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
] ]
[[package]]
name = "packaging"
version = "26.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
]
[[package]] [[package]]
name = "pathspec" name = "pathspec"
version = "1.1.1" version = "1.1.1"
@@ -234,6 +275,42 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" },
] ]
[[package]]
name = "pluggy"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "pygments"
version = "2.20.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
]
[[package]]
name = "pytest"
version = "9.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
]
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.16.0" version = "0.16.0"