diff --git a/tests/test_core.py b/tests/test_core.py index aaf2faf..bfc026e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2,6 +2,7 @@ proper test functions with tmp_path/monkeypatch fixtures).""" import json +import os import sys import pytest @@ -520,6 +521,42 @@ def test_config_mtime_returns_none_for_missing_file(tmp_path): assert c.config_mtime(tmp_path / "nonexistent.json") is None +def test_config_fingerprint_returns_mtime_and_size_for_existing_file(tmp_path): + f = tmp_path / "cfg.json" + f.write_text("{}") + fp = c.config_fingerprint(f) + assert isinstance(fp, c.ConfigStat) + assert isinstance(fp.mtime, float) + assert fp.size == f.stat().st_size + + +def test_config_fingerprint_returns_none_for_missing_file(tmp_path): + assert c.config_fingerprint(tmp_path / "nonexistent.json") is None + + +def test_config_fingerprint_detects_size_change_when_mtime_is_unchanged(tmp_path): + """ + Guards against the exact hole bare-mtime comparison has: an external write + that lands within the filesystem's mtime resolution (or that restores the + original mtime) must still be caught, because the file's size differs. + """ + f = tmp_path / "cfg.json" + f.write_text('{"mcpServers": {}}') + original = c.config_fingerprint(f) + original_mtime_ns = f.stat().st_mtime_ns + + # Overwrite with materially different (larger) content, then force the + # mtime back to its original value -- simulating a same-second external + # write, or a writer that preserves mtime. + f.write_text('{"mcpServers": {"new-server": {"command": "node", "args": ["a", "b", "c"]}}}') + os.utime(f, ns=(original_mtime_ns, original_mtime_ns)) + + updated = c.config_fingerprint(f) + assert updated.mtime == original.mtime # mtime alone would say "unchanged" + assert updated.size != original.size # size catches what mtime missed + assert updated != original # the fingerprint as a whole detects the change + + def test_external_change_summary_detects_non_server_key_change(tmp_path): cfgpath = tmp_path / "cfg.json" original = {"numStartups": 1, "mcpServers": {"s": {"command": "node"}}}