Skip to content

Commit 897cc01

Browse files
authored
fix(seed): coerce every *_date field, not just release_date (#49)
_load_dir turned "release_date" strings into date objects by name, so a category whose date column is named differently reached SQLite as a str: seeding data/website failed with TypeError: SQLite Date type only accepts Python date objects as input on website.launch_date. Coercion now keys on the _date suffix. The endpoint tests for each category insert fixtures through the model with real date objects, so none of them covered the JSON -> DB path where this happens; test_seed_date_coercion.py now does. Verified by seeding the 40,084-record data/website tree: 40,084 rows inserted.
1 parent f7b49b2 commit 897cc01

2 files changed

Lines changed: 61 additions & 2 deletions

File tree

app/seed.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,12 @@ def _load_dir(subdir: Path) -> list[dict[str, Any]]:
4646
for path in sorted(subdir.rglob("*.json")): # recurse into brand subfolders
4747
record = json.loads(path.read_text(encoding="utf-8"))
4848
# SQLModel table models skip validation, so coerce ISO date strings here.
49-
if isinstance(record.get("release_date"), str):
50-
record["release_date"] = date.fromisoformat(record["release_date"])
49+
# Keyed on the *_date suffix rather than one field name: a category whose
50+
# date column is not called release_date (website.launch_date) would
51+
# otherwise reach SQLite as a str and fail the insert.
52+
for key, value in list(record.items()):
53+
if key.endswith("_date") and isinstance(value, str):
54+
record[key] = date.fromisoformat(value)
5155
items.append(record)
5256
return items
5357

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""The seed loader must turn ISO date strings into date objects.
2+
3+
Regression test for a category whose date column is not named ``release_date``:
4+
websites use ``launch_date``, and while the field-name-specific coercion was in
5+
place such a record reached SQLite as a str and the insert failed with
6+
"SQLite Date type only accepts Python date objects as input".
7+
8+
The endpoint tests insert fixtures through the model with real date objects, so
9+
they never covered the JSON -> DB path this exercises.
10+
"""
11+
12+
from __future__ import annotations
13+
14+
import json
15+
from datetime import date
16+
from pathlib import Path
17+
18+
from app.seed import _load_dir
19+
20+
21+
def test_load_dir_coerces_any_date_suffixed_field(tmp_path: Path) -> None:
22+
(tmp_path / "a.json").write_text(
23+
json.dumps(
24+
{
25+
"slug": "example-site",
26+
"name": "Example",
27+
"launch_date": "2001-01-15",
28+
"release_date": "1998-01-02",
29+
"verified": False,
30+
"source_urls": ["https://example.com"],
31+
}
32+
),
33+
encoding="utf-8",
34+
)
35+
36+
records = _load_dir(tmp_path)
37+
38+
assert len(records) == 1
39+
assert records[0]["launch_date"] == date(2001, 1, 15)
40+
assert records[0]["release_date"] == date(1998, 1, 2)
41+
42+
43+
def test_load_dir_leaves_non_date_fields_alone(tmp_path: Path) -> None:
44+
(tmp_path / "a.json").write_text(
45+
json.dumps({"slug": "x", "name": "X", "homepage_url": "https://x.example"}),
46+
encoding="utf-8",
47+
)
48+
49+
records = _load_dir(tmp_path)
50+
51+
assert records[0]["homepage_url"] == "https://x.example"
52+
53+
54+
def test_load_dir_missing_directory_is_empty(tmp_path: Path) -> None:
55+
assert _load_dir(tmp_path / "nope") == []

0 commit comments

Comments
 (0)