mirror of
https://github.com/rendercv/rendercv.git
synced 2026-04-17 21:39:00 -04:00
- Revert entry_with_date.py to let ValueError propagate naturally (Pydantic produces better messages like "The month must be between 1 and 12." than our generic wrapper) - Update create_theme tests to expect typer.Exit (now has error handler) - Update pdf_png test to use real temp file instead of patching read_text - Fix watcher test mock to handle double join() call pattern - Regenerate JSON schema after union type order change
142 lines
4.4 KiB
Python
142 lines
4.4 KiB
Python
import threading
|
|
import time
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import typer
|
|
import watchdog.events
|
|
|
|
from rendercv.cli.render_command import watcher
|
|
from rendercv.cli.render_command.watcher import EventHandler
|
|
|
|
|
|
class TestRunFunctionIfFilesChange:
|
|
def test_runs_function_immediately_on_start(self, tmp_path):
|
|
file_path = tmp_path / "test.yaml"
|
|
file_path.touch()
|
|
mock_function = MagicMock()
|
|
|
|
mock_observer_class = MagicMock()
|
|
# First join() raises KeyboardInterrupt; second join() (after stop) succeeds:
|
|
mock_observer_class.return_value.join.side_effect = [KeyboardInterrupt, None]
|
|
with patch.object(watcher.watchdog.observers, "Observer", mock_observer_class):
|
|
watcher.run_function_if_files_change([file_path], mock_function)
|
|
|
|
mock_function.assert_called_once()
|
|
|
|
def test_reruns_function_when_file_is_modified(self, tmp_path):
|
|
watched_file = tmp_path / "test.yaml"
|
|
watched_file.write_text("initial", encoding="utf-8")
|
|
|
|
call_count = 0
|
|
|
|
def tracked_function():
|
|
nonlocal call_count
|
|
call_count += 1
|
|
|
|
watcher_thread = threading.Thread(
|
|
target=watcher.run_function_if_files_change,
|
|
args=([watched_file], tracked_function),
|
|
daemon=True,
|
|
)
|
|
watcher_thread.start()
|
|
|
|
time.sleep(0.2)
|
|
initial_count = call_count
|
|
|
|
watched_file.write_text("first edit", encoding="utf-8")
|
|
time.sleep(0.2)
|
|
|
|
assert call_count > initial_count
|
|
|
|
def test_reruns_function_when_secondary_file_is_modified(self, tmp_path):
|
|
main_file = tmp_path / "cv.yaml"
|
|
main_file.write_text("main content", encoding="utf-8")
|
|
design_file = tmp_path / "design.yaml"
|
|
design_file.write_text("initial design", encoding="utf-8")
|
|
|
|
call_count = 0
|
|
|
|
def tracked_function():
|
|
nonlocal call_count
|
|
call_count += 1
|
|
|
|
watcher_thread = threading.Thread(
|
|
target=watcher.run_function_if_files_change,
|
|
args=([main_file, design_file], tracked_function),
|
|
daemon=True,
|
|
)
|
|
watcher_thread.start()
|
|
|
|
time.sleep(0.2)
|
|
count_before_edit = call_count
|
|
|
|
# Edit the design file (not the main file)
|
|
design_file.write_text("updated design", encoding="utf-8")
|
|
time.sleep(0.2)
|
|
|
|
assert call_count > count_before_edit
|
|
|
|
def test_continues_watching_after_typer_exit(self, tmp_path):
|
|
watched_file = tmp_path / "test.yaml"
|
|
watched_file.write_text("initial", encoding="utf-8")
|
|
|
|
call_count = 0
|
|
should_raise = False
|
|
|
|
def tracked_function():
|
|
nonlocal call_count
|
|
call_count += 1
|
|
if should_raise:
|
|
raise typer.Exit(code=1)
|
|
|
|
watcher_thread = threading.Thread(
|
|
target=watcher.run_function_if_files_change,
|
|
args=([watched_file], tracked_function),
|
|
daemon=True,
|
|
)
|
|
watcher_thread.start()
|
|
|
|
time.sleep(0.2)
|
|
should_raise = True
|
|
count_before_exit = call_count
|
|
watched_file.write_text("edit that raises exit", encoding="utf-8")
|
|
time.sleep(0.2)
|
|
|
|
assert call_count > count_before_exit
|
|
|
|
should_raise = False
|
|
count_after_exit = call_count
|
|
watched_file.write_text("edit after exit", encoding="utf-8")
|
|
time.sleep(0.2)
|
|
|
|
assert call_count > count_after_exit
|
|
|
|
|
|
class TestEventHandler:
|
|
def test_ignores_events_for_unwatched_files(self):
|
|
mock_fn = MagicMock()
|
|
handler = EventHandler(mock_fn, watched_files={"/watched/file.yaml"})
|
|
|
|
event = watchdog.events.FileModifiedEvent("/other/file.yaml")
|
|
handler.on_modified(event)
|
|
|
|
mock_fn.assert_not_called()
|
|
|
|
def test_calls_function_for_watched_file(self):
|
|
mock_fn = MagicMock()
|
|
handler = EventHandler(mock_fn, watched_files={"/watched/file.yaml"})
|
|
|
|
event = watchdog.events.FileModifiedEvent("/watched/file.yaml")
|
|
handler.on_modified(event)
|
|
|
|
mock_fn.assert_called_once()
|
|
|
|
def test_suppresses_typer_exit(self):
|
|
mock_fn = MagicMock(side_effect=typer.Exit(code=1))
|
|
handler = EventHandler(mock_fn, watched_files={"/watched/file.yaml"})
|
|
|
|
event = watchdog.events.FileModifiedEvent("/watched/file.yaml")
|
|
handler.on_modified(event)
|
|
|
|
mock_fn.assert_called_once()
|