fix(library): an out-of-range last use reads as unknown, not a failed list

This commit is contained in:
Vito Sansevero
2026-10-19 23:05:34 -07:00
parent 73d7b9490b
commit bdd6a5cbe4
3 changed files with 52 additions and 5 deletions
+10 -5
View File
@@ -51,7 +51,12 @@ def utc_iso(value: object) -> str | None:
"""A stored timestamp as UTC ISO text, or None when absent or unreadable.
Usage writes are already UTC ISO; a naive value (SQLite's CURRENT_TIMESTAMP
form) is read as UTC, and an offset is converted to UTC.
form) is read as UTC, and an offset is converted to UTC. One bad row must
not fail a whole Library read, so a value past the datetime range after
conversion (OverflowError) is unreadable too. Python 3.11+ fromisoformat
accepts more ISO forms than 3.10 (e.g. a 2-digit fraction); every form the
app writes (utc_now_iso, the 3.2.4 backfill, CURRENT_TIMESTAMP, Z) parses
the same on both, so only hand-edited values can differ by version.
"""
if not isinstance(value, str):
return None
@@ -60,11 +65,11 @@ def utc_iso(value: object) -> str | None:
text = text[:-1] + "+00:00"
try:
stamp = datetime.datetime.fromisoformat(text)
except ValueError:
if stamp.tzinfo is None:
stamp = stamp.replace(tzinfo=datetime.timezone.utc)
return stamp.astimezone(datetime.timezone.utc).isoformat()
except (ValueError, OverflowError):
return None
if stamp.tzinfo is None:
return stamp.replace(tzinfo=datetime.timezone.utc).isoformat()
return stamp.astimezone(datetime.timezone.utc).isoformat()
_NAME = (
+32
View File
@@ -468,6 +468,38 @@ class TestLibraryAPI(ImageAPITestCase):
)
return response, await response.json()
async def test_unconvertible_last_use_does_not_fail_list_detail_or_receipt(self):
positive = self._save_prompt("Cat")
other = self._save_prompt("Dog")
with self.api.db.model.get_connection() as connection:
connection.execute(
"UPDATE prompts SET last_used_at=? WHERE id=?",
("9999-12-31T23:59:59-01:00", positive),
)
connection.execute(
"UPDATE prompts SET last_used_at=? WHERE id=?",
("0001-01-01T00:00:00+01:00", other),
)
response, listed = await self.get()
self.assertEqual(response.status, 200, listed)
self.assertEqual(
[item["last_used_at"] for item in listed["items"]], [None, None]
)
response, detail = await self.get(f"/{positive}")
self.assertEqual(response.status, 200, detail)
self.assertIsNone(detail["positive"]["last_used_at"])
response, receipt = await self.patch_parent(
positive,
{
"expected_epoch": detail["library_epoch"],
"if_match": detail["positive"]["edit_token"],
"metadata": {"title": "Portrait"},
},
)
self.assertEqual(response.status, 200, receipt)
self.assertEqual(receipt["positive"]["title"], "Portrait")
self.assertIsNone(receipt["positive"]["last_used_at"])
async def test_native_parent_patch_strict_validation(self):
from database.execution_history_schema import rotate_history_epoch
+10
View File
@@ -498,10 +498,15 @@ class TestLibrary(unittest.TestCase):
offset = self.save("Offset")
broken = self.save("Broken")
zulu = self.save("Zulu")
# Parse but cannot convert to UTC: past the datetime range either way.
floor = self.save("Floor")
ceiling = self.save("Ceiling")
with self.db.model.get_connection() as connection:
for prompt_id, value in (
(legacy, "2026-10-07 18:39:06"),
(zulu, "2026-10-07T18:39:06Z"),
(floor, "0001-01-01T00:00:00+01:00"),
(ceiling, "9999-12-31T23:59:59-01:00"),
(offset, "2026-10-07T20:39:06.250+02:00"),
(broken, "yesterday"),
):
@@ -525,6 +530,11 @@ class TestLibrary(unittest.TestCase):
)
self.assertIsNone(items[broken]["last_used_at"])
self.assertEqual(items[zulu]["last_used_at"], "2026-10-07T18:39:06+00:00")
self.assertIsNone(items[floor]["last_used_at"])
self.assertIsNone(items[ceiling]["last_used_at"])
self.assertIsNone(
self.db.get_library_positive(floor)["positive"]["last_used_at"]
)
self.assertEqual(type(items[broken]["run_count"]), int)
detail = self.db.get_library_positive(used)["positive"]
self.assertEqual(detail["run_count"], 3)