Skip to content

Commit 619933f

Browse files
committed
feat(docs): update README to reflect version change to 0.0.2 and add unit tests section
feat(tests): add unit tests for ZLog functionality including logging levels and network requests chore(tests): create test directory structure and cleanup fixture for test logs feat(assets): add screenshot for unit tests results to README feat(zlogger_kit): add ZModule enum for better log module management
1 parent 7400169 commit 619933f

File tree

6 files changed

+191
-1
lines changed

6 files changed

+191
-1
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![Downloads](https://img.shields.io/pypi/dm/zlogger-kit)](https://pypi.org/project/zlogger-kit/)
88
[![PyPI version](https://img.shields.io/pypi/v/zlogger-kit)](https://img.shields.io/pypi/v/zlogger-kit)
99
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10-
![ZLogger Kit](https://img.shields.io/badge/ZLogger_Kit-0.0.1-blue)
10+
![ZLogger Kit](https://img.shields.io/badge/ZLogger_Kit-0.0.2-blue)
1111
![Python](https://img.shields.io/badge/Python->=3.11,<4.0-blue)
1212
![FastAPI](https://img.shields.io/badge/FastAPI->=0.109.0,<0.115.8-blue)
1313
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -248,6 +248,19 @@ poetry run uvicorn examples.example2:app --reload
248248
{"timestamp": "2025-02-09T01:26:48.971760+03:00", "module": "PAYMENT", "priority": "P20", "message": "GET http://127.0.0.1:8000/health", "level": "INFO", "operation": "request", "method": "GET", "url": "http://127.0.0.1:8000/health", "ip": "127.0.0.1"}
249249
```
250250

251+
## Unit Tests
252+
253+
```bash
254+
poetry run pytest
255+
```
256+
257+
![Unit Tests](./assets/1.png)
258+
259+
260+
261+
262+
263+
251264
## Contributing
252265

253266
Contributions are welcome! Please feel free to submit a PR.

assets/1.png

102 KB
Loading

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Empty init file to make the tests directory a Python package

tests/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import pytest
2+
import os
3+
4+
5+
@pytest.fixture(autouse=True)
6+
def cleanup_test_logs():
7+
"""Clean up test logs before and after each test"""
8+
test_dir = "test_logs"
9+
if os.path.exists(test_dir):
10+
for file in os.listdir(test_dir):
11+
os.remove(os.path.join(test_dir, file))
12+
os.rmdir(test_dir)
13+
14+
yield
15+
16+
if os.path.exists(test_dir):
17+
for file in os.listdir(test_dir):
18+
os.remove(os.path.join(test_dir, file))
19+
os.rmdir(test_dir)

tests/test_zlog.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import os
2+
import json
3+
from datetime import datetime
4+
from unittest import TestCase
5+
from zoneinfo import ZoneInfo
6+
7+
from zlogger_kit.zlog import ZLog
8+
from zlogger_kit.models import ZLogConfig, ZNetworkRequest, ZNetworkResponse
9+
from zlogger_kit.enums import ZLogLevel, ZNetworkOperation, ZModule
10+
11+
12+
class TestZLog(TestCase):
13+
def setUp(self):
14+
self.test_dir = "test_logs"
15+
self.config = ZLogConfig(
16+
module=ZModule.TEST_JSON_FORMAT,
17+
log_path=self.test_dir,
18+
time_zone="Asia/Riyadh",
19+
json_format=True,
20+
)
21+
os.makedirs(self.test_dir, exist_ok=True)
22+
self.logger = ZLog.init(self.config)
23+
24+
def tearDown(self):
25+
for file in os.listdir(self.test_dir):
26+
os.remove(os.path.join(self.test_dir, file))
27+
os.rmdir(self.test_dir)
28+
ZLog._instances = {}
29+
30+
def test_singleton_pattern(self):
31+
logger1 = ZLog.init(self.config)
32+
logger2 = ZLog.init(self.config)
33+
self.assertIs(logger1, logger2)
34+
35+
other_config = ZLogConfig(
36+
module=ZModule.OTHER,
37+
log_path=self.test_dir,
38+
time_zone="UTC",
39+
json_format=True,
40+
)
41+
logger3 = ZLog.init(other_config)
42+
self.assertIsNot(logger1, logger3)
43+
44+
def test_log_file_creation(self):
45+
test_time = datetime(2024, 1, 1, tzinfo=ZoneInfo("Asia/Riyadh"))
46+
self.logger.set_current_time(test_time)
47+
self.logger.info("Test message")
48+
49+
expected_filename = f"{ZModule.TEST_JSON_FORMAT.value}-2024-01-01.log"
50+
self.assertTrue(os.path.exists(os.path.join(self.test_dir, expected_filename)))
51+
52+
def test_log_levels(self):
53+
test_time = datetime(2024, 1, 1, tzinfo=ZoneInfo("Asia/Riyadh"))
54+
self.logger.set_current_time(test_time)
55+
56+
test_message = "Test message"
57+
self.logger.debug(test_message)
58+
self.logger.info(test_message)
59+
self.logger.warn(test_message)
60+
self.logger.error(test_message)
61+
62+
log_file = os.path.join(
63+
self.test_dir, f"{ZModule.TEST_JSON_FORMAT.value}-2024-01-01.log"
64+
)
65+
with open(log_file, "r") as f:
66+
logs = f.readlines()
67+
68+
self.assertEqual(len(logs), 4)
69+
for log, level in zip(
70+
logs, [ZLogLevel.DEBUG, ZLogLevel.INFO, ZLogLevel.WARNING, ZLogLevel.ERROR]
71+
):
72+
log_data = json.loads(log)
73+
self.assertEqual(log_data["level"], level.value)
74+
self.assertEqual(log_data["message"], test_message)
75+
76+
def test_error_logging(self):
77+
test_error = ValueError("Test error")
78+
self.logger.error("Error occurred", error=test_error)
79+
80+
log_file = os.path.join(
81+
self.test_dir,
82+
f"{ZModule.TEST_JSON_FORMAT.value}-{datetime.now().strftime('%Y-%m-%d')}.log",
83+
)
84+
with open(log_file, "r") as f:
85+
log_data = json.loads(f.readline())
86+
87+
self.assertEqual(log_data["error"], str(test_error))
88+
self.assertEqual(log_data["level"], ZLogLevel.ERROR.value)
89+
90+
def test_network_logging(self):
91+
test_time = datetime(2024, 1, 1, tzinfo=ZoneInfo("Asia/Riyadh"))
92+
self.logger.set_current_time(test_time)
93+
94+
request = ZNetworkRequest(method="GET", url="https://api.example.com")
95+
self.logger.network_request(request, ip="127.0.0.1")
96+
97+
response = ZNetworkResponse(status_code=200)
98+
self.logger.network_response(response, ip="127.0.0.1")
99+
100+
log_file = os.path.join(
101+
self.test_dir, f"{ZModule.TEST_JSON_FORMAT.value}-2024-01-01.log"
102+
)
103+
with open(log_file, "r") as f:
104+
logs = f.readlines()
105+
106+
request_log = json.loads(logs[0])
107+
response_log = json.loads(logs[1])
108+
109+
self.assertEqual(request_log["operation"], ZNetworkOperation.REQUEST.value)
110+
self.assertEqual(request_log["method"], "GET")
111+
self.assertEqual(request_log["url"], "https://api.example.com")
112+
self.assertEqual(request_log["ip"], "127.0.0.1")
113+
114+
self.assertEqual(response_log["operation"], ZNetworkOperation.RESPONSE.value)
115+
self.assertEqual(response_log["status_code"], 200)
116+
self.assertEqual(response_log["ip"], "127.0.0.1")
117+
118+
def test_text_format_logging(self):
119+
config = ZLogConfig(
120+
module=ZModule.TEST_TEXT_FORMAT,
121+
log_path=self.test_dir,
122+
time_zone="Asia/Riyadh",
123+
json_format=False,
124+
)
125+
logger = ZLog.init(config)
126+
test_time = datetime(2024, 1, 1, tzinfo=ZoneInfo("Asia/Riyadh"))
127+
logger.set_current_time(test_time)
128+
129+
test_message = "Test message"
130+
logger.info(test_message)
131+
132+
log_file = os.path.join(self.test_dir, "test_text_format-2024-01-01.log")
133+
with open(log_file, "r") as f:
134+
log_line = f.readline().strip()
135+
136+
expected_components = [
137+
"2024-01-01",
138+
"[INFO]",
139+
"Test",
140+
test_message,
141+
]
142+
143+
for component in expected_components:
144+
self.assertIn(component, log_line)
145+
146+
def _is_json(self, string):
147+
try:
148+
json.loads(string)
149+
return True
150+
except json.JSONDecodeError:
151+
return False

zlogger_kit/enums.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,9 @@ def __new__(cls, value, priority):
5050
obj._value_ = value
5151
obj.priority = priority
5252
return obj
53+
54+
55+
class ZModule(str, Enum):
56+
TEST_JSON_FORMAT = "test_json_format"
57+
TEST_TEXT_FORMAT = "test_text_format"
58+
OTHER = "other_module"

0 commit comments

Comments
 (0)