Coverage for tests / unit / utils / test_file_cache.py: 100%
97 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Tests for lintro.utils.file_cache module."""
3from __future__ import annotations
5import json
6from pathlib import Path
7from unittest.mock import patch
9from assertpy import assert_that
11from lintro.utils.file_cache import (
12 FileFingerprint,
13 ToolCache,
14 clear_all_caches,
15 get_cache_stats,
16)
19def test_file_fingerprint_to_dict() -> None:
20 """Convert fingerprint to dictionary."""
21 fp = FileFingerprint(path="/test/file.py", mtime=1234567890.0, size=1024)
22 result = fp.to_dict()
24 assert_that(result).is_equal_to(
25 {
26 "path": "/test/file.py",
27 "mtime": 1234567890.0,
28 "size": 1024,
29 },
30 )
33def test_file_fingerprint_from_dict() -> None:
34 """Create fingerprint from dictionary."""
35 data = {"path": "/test/file.py", "mtime": 1234567890.0, "size": 1024}
36 fp = FileFingerprint.from_dict(data)
38 assert_that(fp.path).is_equal_to("/test/file.py")
39 assert_that(fp.mtime).is_equal_to(1234567890.0)
40 assert_that(fp.size).is_equal_to(1024)
43def test_file_fingerprint_roundtrip() -> None:
44 """Roundtrip through to_dict and from_dict."""
45 original = FileFingerprint(path="/test/file.py", mtime=1234567890.0, size=1024)
46 result = FileFingerprint.from_dict(original.to_dict())
48 assert_that(result.path).is_equal_to(original.path)
49 assert_that(result.mtime).is_equal_to(original.mtime)
50 assert_that(result.size).is_equal_to(original.size)
53def test_tool_cache_empty_returns_all_files_as_changed(tmp_path: Path) -> None:
54 """Empty cache returns all files as changed.
56 Args:
57 tmp_path: Pytest fixture for temporary directory.
58 """
59 temp_file = tmp_path / "test.py"
60 temp_file.write_text("test content")
62 cache = ToolCache(tool_name="test")
63 changed = cache.get_changed_files([str(temp_file)])
64 assert_that(changed).contains(str(temp_file))
67def test_tool_cache_unchanged_file_not_returned(tmp_path: Path) -> None:
68 """File in cache with same mtime/size not returned as changed.
70 Args:
71 tmp_path: Pytest fixture for temporary directory.
72 """
73 temp_file = tmp_path / "test.py"
74 temp_file.write_text("test content")
76 stat = temp_file.stat()
77 cache = ToolCache(tool_name="test")
78 cache.fingerprints[str(temp_file)] = FileFingerprint(
79 path=str(temp_file),
80 mtime=stat.st_mtime,
81 size=stat.st_size,
82 )
83 changed = cache.get_changed_files([str(temp_file)])
84 assert_that(changed).does_not_contain(str(temp_file))
87def test_tool_cache_modified_file_returned(tmp_path: Path) -> None:
88 """File in cache with different mtime returned as changed.
90 Args:
91 tmp_path: Pytest fixture for temporary directory.
92 """
93 temp_file = tmp_path / "test.py"
94 temp_file.write_text("test content")
96 stat = temp_file.stat()
97 cache = ToolCache(tool_name="test")
98 cache.fingerprints[str(temp_file)] = FileFingerprint(
99 path=str(temp_file),
100 mtime=stat.st_mtime - 100,
101 size=stat.st_size,
102 )
103 changed = cache.get_changed_files([str(temp_file)])
104 assert_that(changed).contains(str(temp_file))
107def test_tool_cache_size_changed_file_returned(tmp_path: Path) -> None:
108 """File in cache with different size returned as changed.
110 Args:
111 tmp_path: Pytest fixture for temporary directory.
112 """
113 temp_file = tmp_path / "test.py"
114 temp_file.write_text("test content")
116 stat = temp_file.stat()
117 cache = ToolCache(tool_name="test")
118 cache.fingerprints[str(temp_file)] = FileFingerprint(
119 path=str(temp_file),
120 mtime=stat.st_mtime,
121 size=stat.st_size + 100, # Different size
122 )
123 changed = cache.get_changed_files([str(temp_file)])
124 assert_that(changed).contains(str(temp_file))
127def test_tool_cache_nonexistent_file_skipped() -> None:
128 """Nonexistent file skipped in get_changed_files."""
129 cache = ToolCache(tool_name="test")
130 changed = cache.get_changed_files(["/nonexistent/file.py"])
131 assert_that(changed).is_empty()
134def test_tool_cache_update_adds_fingerprints(tmp_path: Path) -> None:
135 """Update adds fingerprints for files.
137 Args:
138 tmp_path: Pytest fixture for temporary directory.
139 """
140 temp_file = tmp_path / "test.py"
141 temp_file.write_text("test content")
143 cache = ToolCache(tool_name="test")
144 cache.update([str(temp_file)])
145 assert_that(cache.fingerprints).contains_key(str(temp_file))
148def test_tool_cache_clear_removes_all_fingerprints() -> None:
149 """Clear removes all fingerprints."""
150 cache = ToolCache(tool_name="test")
151 cache.fingerprints["file1.py"] = FileFingerprint(
152 path="file1.py",
153 mtime=1234567890.0,
154 size=100,
155 )
156 cache.clear()
157 assert_that(cache.fingerprints).is_empty()
160def test_tool_cache_save_and_load_roundtrip(tmp_path: Path) -> None:
161 """Save and load preserves cache data.
163 Args:
164 tmp_path: Pytest fixture for temporary directory.
165 """
166 with patch("lintro.utils.file_cache.CACHE_DIR", tmp_path):
167 cache = ToolCache(tool_name="test_tool")
168 cache.fingerprints["/test/file.py"] = FileFingerprint(
169 path="/test/file.py",
170 mtime=1234567890.0,
171 size=1024,
172 )
173 cache.save()
175 loaded = ToolCache.load("test_tool")
176 assert_that(loaded.tool_name).is_equal_to("test_tool")
177 assert_that(loaded.fingerprints).contains_key("/test/file.py")
180def test_tool_cache_load_returns_empty_for_missing_file(tmp_path: Path) -> None:
181 """Load returns empty cache for missing cache file.
183 Args:
184 tmp_path: Pytest fixture for temporary directory.
185 """
186 with patch("lintro.utils.file_cache.CACHE_DIR", tmp_path):
187 loaded = ToolCache.load("nonexistent_tool")
188 assert_that(loaded.fingerprints).is_empty()
191def test_clear_all_caches_deletes_files(tmp_path: Path) -> None:
192 """Clear all caches deletes all cache files.
194 Args:
195 tmp_path: Pytest fixture for temporary directory.
196 """
197 (tmp_path / "tool1.json").write_text('{"tool_name": "tool1"}')
198 (tmp_path / "tool2.json").write_text('{"tool_name": "tool2"}')
200 with patch("lintro.utils.file_cache.CACHE_DIR", tmp_path):
201 clear_all_caches()
202 assert_that(list(tmp_path.glob("*.json"))).is_empty()
205def test_get_cache_stats_returns_file_counts(tmp_path: Path) -> None:
206 """Get cache stats returns file counts.
208 Args:
209 tmp_path: Pytest fixture for temporary directory.
210 """
211 cache_data = {
212 "tool_name": "test_tool",
213 "fingerprints": {
214 "/file1.py": {"path": "/file1.py", "mtime": 1.0, "size": 100},
215 "/file2.py": {"path": "/file2.py", "mtime": 2.0, "size": 200},
216 },
217 }
218 (tmp_path / "test_tool.json").write_text(json.dumps(cache_data))
220 with patch("lintro.utils.file_cache.CACHE_DIR", tmp_path):
221 stats = get_cache_stats()
222 assert_that(stats).contains_key("test_tool")
223 assert_that(stats["test_tool"]).is_equal_to(2)
226def test_get_cache_stats_returns_empty_for_nonexistent_dir(tmp_path: Path) -> None:
227 """Get cache stats returns empty dict for nonexistent directory.
229 Args:
230 tmp_path: Pytest fixture for temporary directory.
231 """
232 nonexistent_dir = tmp_path / "nonexistent"
234 with patch("lintro.utils.file_cache.CACHE_DIR", nonexistent_dir):
235 stats = get_cache_stats()
236 assert_that(stats).is_empty()