Skip to content

Commit 4d47b9b

Browse files
authored
feat: don't clear logs when sending failed + send them in text format (#6)
* Don't send the buffer when notifying failed * Change body format to TEXT * Ignore extra stuff
1 parent 49061f9 commit 4d47b9b

File tree

4 files changed

+116
-8
lines changed

4 files changed

+116
-8
lines changed

logprise/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import apprise.cli
1212
import loguru._logger
13+
from apprise.common import NotifyFormat
1314
from loguru import logger
1415

1516

@@ -170,9 +171,9 @@ def send_notification(self) -> None:
170171
return
171172

172173
# Format the buffered logs into a single message
173-
message = "".join(self.buffer)
174-
self.apprise_obj.notify(title="Script Notifications", body=message)
175-
self.buffer.clear() # Clear the buffer after sending
174+
message = "".join(self.buffer).replace("\r", "")
175+
if self.apprise_obj.notify(title="Script Notifications", body=message, body_format=NotifyFormat.TEXT):
176+
self.buffer.clear() # Clear the buffer after sending
176177

177178

178179
appriser = Appriser()

pyproject.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ skip-magic-trailing-comma = false
123123
line-ending = "auto"
124124

125125
[tool.coverage.run]
126-
source = ["minecraft_log_watcher"]
126+
source = ["logprise"]
127127
branch = true
128128

129129
[tool.coverage.report]
130-
exclude_lines = [
130+
exclude_also = [
131131
"pragma: no cover",
132132
"def __repr__",
133133
"raise NotImplementedError",
@@ -137,4 +137,8 @@ exclude_lines = [
137137
"@abstractmethod",
138138
"@abc.abstractmethod"
139139
]
140-
show_missing = true
140+
show_missing = true
141+
142+
[tool.pytest.ini_options]
143+
#addopts = "--no-cov" # This disables coverage
144+
addopts = "--cov=logprise --cov-branch --cov-report=html --cov-report=xml --cov-report=term"

tests/conftest.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
1+
import contextlib
2+
13
import pytest
24
from apprise import Apprise
35

6+
import logprise
7+
48

5-
@pytest.fixture
9+
@pytest.fixture(autouse=True)
610
def notify_mock(monkeypatch):
711
"""Only mock the notify method of Apprise"""
812
calls = []
913

10-
def mock_notify(self, title, body):
14+
def mock_notify(self, title, body, *_, **__):
1115
calls.append({"title": title, "body": body})
1216
return True
1317

1418
monkeypatch.setattr(Apprise, "notify", mock_notify)
1519
return calls
20+
21+
22+
@pytest.fixture(autouse=True)
23+
def silence_logger():
24+
with contextlib.suppress(ValueError):
25+
logprise.logger.remove(0)

tests/test_periodic_flush.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,96 @@ def test_flush_only_if_buffer_has_content(notify_mock, monkeypatch):
148148

149149
# Now send_notification should be called
150150
mock_send.assert_called_once()
151+
152+
153+
def test_periodic_flush_stops_on_event_set(mocker):
154+
"""Test that periodic_flush exits when stop_event is set."""
155+
appriser = Appriser()
156+
157+
# Pre-set the stop event before calling _periodic_flush
158+
appriser._stop_event.set()
159+
160+
# Add a log message to ensure buffer has content
161+
logger.error("Test message")
162+
163+
# Mock send_notification to track calls
164+
mock_send = mocker.patch.object(appriser, "send_notification")
165+
166+
# Run the periodic flush - it should exit immediately
167+
appriser._periodic_flush()
168+
169+
# Verify send_notification was not called
170+
mock_send.assert_not_called()
171+
172+
# Verify message still in buffer (didn't get cleared)
173+
assert len(appriser.buffer) == 1
174+
assert "Test message" in appriser.buffer[0].record["message"]
175+
176+
177+
def test_periodic_flush_empty_buffer(mocker):
178+
"""Test that periodic_flush skips sending when buffer is empty."""
179+
appriser = Appriser()
180+
181+
# Clear buffer
182+
appriser.buffer.clear()
183+
184+
# Mock send_notification to track calls
185+
mock_send = mocker.patch.object(appriser, "send_notification")
186+
187+
# Set up stop_event to trigger after a short delay
188+
def set_stop_after_delay():
189+
time.sleep(0.1) # Short delay
190+
appriser._stop_event.set()
191+
192+
# Start a thread that will set the stop event after a delay
193+
thread = threading.Thread(target=set_stop_after_delay)
194+
thread.start()
195+
196+
# Run periodic flush - it should wait until stop_event is set
197+
appriser._periodic_flush()
198+
199+
# Wait for our helper thread to complete
200+
thread.join()
201+
202+
# Verify send_notification was not called since buffer was empty
203+
mock_send.assert_not_called()
204+
205+
206+
def test_stop_periodic_flush_idempotent(mocker):
207+
"""Test that calling stop_periodic_flush twice is safe."""
208+
appriser = Appriser()
209+
210+
# First call
211+
appriser.stop_periodic_flush()
212+
assert appriser._stop_event.is_set()
213+
214+
# Mock thread for second call
215+
mock_thread = mocker.MagicMock()
216+
mock_thread.is_alive.return_value = True
217+
appriser._flush_thread = mock_thread
218+
appriser._stop_event.clear() # Reset to test second call
219+
220+
# Second call
221+
appriser.stop_periodic_flush()
222+
223+
# Verify stop_event is set and join was called
224+
assert appriser._stop_event.is_set()
225+
mock_thread.join.assert_called_once()
226+
227+
228+
def test_notify_failure_preserves_buffer(mocker):
229+
"""Test that buffer is not cleared when notification fails."""
230+
appriser = Appriser()
231+
232+
# Mock apprise_obj.notify to return False (failure)
233+
mocker.patch.object(appriser.apprise_obj, "notify", return_value=False)
234+
235+
# Add test message to buffer
236+
logger.error("Test message that should remain in buffer")
237+
238+
# Try to send notification
239+
appriser.send_notification()
240+
241+
# Verify buffer still contains the message
242+
assert len(appriser.buffer) == 1
243+
assert "Test message that should remain in buffer" in appriser.buffer[0].record["message"]

0 commit comments

Comments
 (0)