fix: harden SessionStore — per-record timestamp handling, atomic writes, robust quarantine

This commit is contained in:
Mathew Sir Guest the best
2026-07-07 21:49:05 -06:00
parent 8a81f16d7d
commit 1909eb621e
3 changed files with 199 additions and 19 deletions
+56
View File
@@ -67,3 +67,59 @@ class TestSessionStore:
assert default_store_path().name == "sessions.json"
assert "wabot" in str(default_store_path())
def test_naive_created_at_is_treated_as_utc_not_crash(self, tmp_path):
# legacy/hand-written records may carry naive timestamps
import json
path = tmp_path / "sessions.json"
record = make_record()
data = {"scraper1": {**record.__dict__, "created_at": "2999-01-01T00:00:00"}}
path.write_text(json.dumps(data))
store = SessionStore(path=path)
assert store.get("scraper1") is not None # fresh: kept, assumed UTC
def test_garbage_created_at_evicts_only_that_record(self, tmp_path):
import json
path = tmp_path / "sessions.json"
good, bad = make_record(name="good"), make_record(name="bad")
data = {
"good": good.__dict__,
"bad": {**bad.__dict__, "created_at": "not-a-date"},
}
path.write_text(json.dumps(data))
store = SessionStore(path=path)
assert store.names() == ["good"] # no crash, bad evicted, file rewritten
assert "not-a-date" not in path.read_text()
def test_null_created_at_evicts_only_that_record(self, tmp_path):
import json
path = tmp_path / "sessions.json"
data = {"broken": {**make_record(name="broken").__dict__, "created_at": None}}
path.write_text(json.dumps(data))
assert SessionStore(path=path).names() == []
def test_unknown_record_fields_are_ignored_not_fatal(self, tmp_path):
# a newer wabot may add fields; older versions must not quarantine the store
import json
path = tmp_path / "sessions.json"
data = {"scraper1": {**make_record().__dict__, "future_field": 42}}
path.write_text(json.dumps(data))
assert SessionStore(path=path).get("scraper1") is not None
def test_repeat_corruption_with_existing_bad_file_does_not_crash(self, tmp_path):
path = tmp_path / "sessions.json"
(tmp_path / "sessions.json.bad").write_text("old corruption")
path.write_text("{ corrupt again")
store = SessionStore(path=path)
assert store.load() == {}
store.save(make_record())
assert store.get("scraper1") is not None
def test_write_is_atomic_no_tmp_leftover(self, tmp_path):
store = SessionStore(path=tmp_path / "sessions.json")
store.save(make_record())
assert not (tmp_path / "sessions.json.tmp").exists()