Files
bindery/tests/test_last_changed.py
tim eea6adcc91 Fix Last Changed updates during ePub rebuilds
Update Last Changed only when Bindery stores a new chapter or adds a new
chapter revision. Manual ePub generation, rebuilds, metadata edits and
rule changes leave it unchanged.

Save the timestamp in the same transaction as the chapter or revision.
This preserves the date if a later build fails and prevents retries from
moving it forward without another chapter change.

Add regression tests for new chapters, revisions, forced rebuilds, failed
builds, retries and transaction rollback. Adjust OPDS test data setup and
document what Last Changed records.
2026-10-06 16:54:33 +02:00

67 lines
3.0 KiB
Python

"""Last Changed records stored chapters and revisions, not changes to the built book."""
import sqlite3
import pytest
from bindery import store as storage
@pytest.fixture
def state(tmp_path, monkeypatch):
clock = ["2024-01-01T00:00:00"]
monkeypatch.setattr(storage, "now", lambda: clock[0])
store = storage.Store(tmp_path / "stories.db")
sid = store.create_story("story", "site", "Title", "Author", [])
return store, sid, clock
@pytest.mark.parametrize("source", ["new", "import"])
def test_new_chapters_and_revisions_set_the_timestamp_in_the_store(state, source):
store, sid, clock = state
other = store.create_story("other", "site", "Other", "Author", [])
assert store.get_story(sid)["last_changed"] is None
first = store.add_chapter(sid, "one", "One", "first", "h1", source, 0)
assert store.get_story(sid)["last_changed"] == clock[0]
assert store.get_chapter(first)["added_at"] == clock[0]
clock[0] = "2024-02-01T00:00:00"
store.add_chapters(sid, [{"url": "two", "title": "Two", "html": "second", "text_hash": "h2"}], source, [])
assert store.get_story(sid)["last_changed"] == clock[0]
clock[0] = "2024-03-01T00:00:00"
store.add_revision(first, "revised", "h3", "recheck")
assert store.get_story(sid)["last_changed"] == clock[0]
assert store.get_chapter(first)["changed_at"] == clock[0]
assert store.list_revisions(first)[-1]["fetched_at"] == clock[0]
assert storage.Store(store.path).get_story(sid)["last_changed"] == clock[0]
assert store.get_story(other)["last_changed"] is None
def test_changes_without_new_chapters_or_revisions_keep_the_timestamp(state):
store, sid, clock = state
first = store.add_chapter(sid, "one", "One", "first", "h1", "new", 0)
second = store.add_chapter(sid, "two", "Two", "second", "h2", "new", 1)
store.add_revision(first, "revised", "h3", "recheck")
previous = clock[0]
clock[0] = "2024-02-01T00:00:00"
store.update_story(sid, title="Edited", pinned={"title": "Edited"})
store.set_rules(sid, [{"pattern": "first", "replacement": "1st"}])
store.update_chapter(first, title="Renamed", included=False, on_site=False, current_rev=1)
store.replace_revision(first, 1, "original site text", "h4")
store.reorder_chapters(sid, lambda order: list(reversed(order)))
store.add_chapters(sid, [], "import", ["one", "two"])
store.delete_revision(first, 2)
store.delete_chapters(sid, [second])
assert store.get_story(sid)["last_changed"] == previous
def test_a_failed_chapter_transaction_does_not_change_the_timestamp(state):
store, sid, clock = state
store.add_chapter(sid, "one", "One", "first", "h1", "new", 0)
previous = clock[0]
clock[0] = "2024-02-01T00:00:00"
chapters = [{"url": url, "title": url, "html": "text", "text_hash": "h"} for url in ("two", "one")]
with pytest.raises(sqlite3.IntegrityError):
store.add_chapters(sid, chapters, "import", [])
assert store.get_story(sid)["last_changed"] == previous
assert [chapter["url"] for chapter in store.list_chapters(sid)] == ["one"]