### Testing FolderNamingService FolderNamingService fixes missing years in anime folder names after NFO refresh. Test file: `tests/unit/test_folder_naming_service.py`. Key methods tested: - `_build_target_folder()` — constructs safe "Title (YYYY)" names, strips existing year suffix first (prevents double-year like "Naruto (1999) (1999)") - `_folder_has_year()` — detects existing `(YYYY)` pattern - `_extract_year_from_folder_name()` — parses `(YYYY)` suffix - `_extract_title_from_folder_name()` — strips year suffix ```python # Safe: repeated calls never accumulate years r1 = FolderNamingService._build_target_folder("Naruto", 1999) r2 = FolderNamingService._build_target_folder(r1, 1999) assert r1 == r2 == "Naruto (1999)" # Safe: existing year is replaced, not appended result = FolderNamingService._build_target_folder("Naruto (2020)", 1999) assert result == "Naruto (1999)" assert "2020" not in result ``` The service is also tested end-to-end with mocked filesystem and database: - Renames folder and updates DB when year is missing from folder name - Skips rename when folder already has a year - Skips rename when DB has no year - Skips when target folder already exists on disk - Safety guard detects and skips if target folder year mismatches DB year ### Mocking aiohttp Sessions When testing code that uses `aiohttp.ClientSession`: ```python from unittest.mock import AsyncMock, MagicMock, patch from aiohttp import ClientSession # Mock aiohttp session for testing class MockAiohttpSession: def __init__(self): self.closed = False async def close(self): self.closed = True def get(self, url, **kwargs): mock_response = AsyncMock() mock_response.status = 200 mock_response.json = AsyncMock(return_value={"data": "test"}) mock_response.__aenter__ = AsyncMock(return_value=mock_response) mock_response.__aexit__ = AsyncMock(return_value=None) return mock_response # Use in fixture @pytest.fixture async def mock_tmdb_session(): session = MockAiohttpSession() yield session # Cleanup verification assert session.closed, "Session was not closed" ``` **Key points:** - Always verify `session.closed` is `True` after context manager exits - Mock `__aenter__` and `__aexit__` for response context managers - Set `closed = False` on mock session for unclosed warning tests 7. Coverage Requirements 8. CI/CD Integration 9. Writing Good Tests - Arrange-Act-Assert pattern - Test isolation - Edge cases 10. Common Pitfalls to Avoid