S1: shared value helpers (parse_dt/coerce_bool) + auto-Secure session cookie
M9 hardening/DRY pass — section S1, commit 1 (the shared-toolkit foundation): - Add src/thoughtsync/common.py with parse_dt() and coerce_bool(): one home for the ISO-date and truthy-flag coercions that were duplicated across modules. notes.py adopts them and deletes _parse_iso_dt, _iso_to_dt and _truthy (rule 22 — old copies removed; callers, incl. tests, updated). - Security: the session cookie is now marked Secure automatically on any request that arrived over HTTPS (directly or via a proxy's X-Forwarded-Proto), via a SecureCookieSessionInterface override. Hardens HTTPS deployments without breaking plain-HTTP LAN installs — no config. Behavior-preserving refactor + one security hardening. The backend serialization layer, the json_error sweep, and the notes.py split follow as their own commits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
+16
-14
@@ -3,15 +3,14 @@ from datetime import datetime, timezone
|
||||
import pytest
|
||||
|
||||
from thoughtsync.app import create_app
|
||||
from thoughtsync.common import coerce_bool, parse_dt
|
||||
from thoughtsync.models.note import NOTE_COLORS, Note
|
||||
from thoughtsync.notes import (
|
||||
_attachment_ext,
|
||||
_escape_like,
|
||||
_header_filename,
|
||||
_truthy,
|
||||
_keep_spec,
|
||||
_native_spec,
|
||||
_parse_iso_dt,
|
||||
_safe_filename,
|
||||
_slugify,
|
||||
_usec_to_dt,
|
||||
@@ -170,18 +169,19 @@ def test_escape_like():
|
||||
assert _escape_like("plain") == "plain"
|
||||
|
||||
|
||||
def test_parse_iso_dt():
|
||||
def test_parse_dt():
|
||||
# A full ISO instant round-trips (used to validate the Timeline date range).
|
||||
d = _parse_iso_dt("2026-07-19T12:30:00+00:00")
|
||||
d = parse_dt("2026-07-19T12:30:00+00:00")
|
||||
assert (d.year, d.month, d.day, d.hour, d.minute) == (2026, 7, 19, 12, 30)
|
||||
assert d.tzinfo is not None
|
||||
# a trailing Z is accepted as UTC
|
||||
assert _parse_iso_dt("2026-07-19T00:00:00Z").tzinfo is not None
|
||||
assert parse_dt("2026-07-19T00:00:00Z").tzinfo is not None
|
||||
# a plain calendar date parses to midnight
|
||||
assert _parse_iso_dt("2026-07-19").hour == 0
|
||||
# garbage raises (the endpoint turns this into a 400)
|
||||
with pytest.raises(ValueError):
|
||||
_parse_iso_dt("not-a-date")
|
||||
assert parse_dt("2026-07-19").hour == 0
|
||||
# garbage / non-strings return None (the endpoint turns this into a 400)
|
||||
assert parse_dt("not-a-date") is None
|
||||
assert parse_dt("") is None
|
||||
assert parse_dt(None) is None
|
||||
|
||||
|
||||
async def test_titles_requires_auth(app):
|
||||
@@ -264,11 +264,13 @@ def test_header_filename():
|
||||
assert _header_filename("") == "file"
|
||||
|
||||
|
||||
def test_truthy():
|
||||
assert _truthy("true") and _truthy("1") and _truthy("yes") and _truthy("on")
|
||||
assert not _truthy("false")
|
||||
assert not _truthy(None)
|
||||
assert not _truthy("")
|
||||
def test_coerce_bool():
|
||||
assert coerce_bool("true") and coerce_bool("1") and coerce_bool("yes") and coerce_bool("on")
|
||||
assert coerce_bool(True)
|
||||
assert not coerce_bool("false")
|
||||
assert not coerce_bool(None)
|
||||
assert not coerce_bool("")
|
||||
assert not coerce_bool(False)
|
||||
|
||||
|
||||
def test_normalize_recurrence():
|
||||
|
||||
Reference in New Issue
Block a user