journal: real markdown editor (EasyMDE) with live preview toggle, fix default template
- Replace the plain textarea with EasyMDE (vendored locally, no CDN) for markdown authoring: syntax highlighting, smart list continuation, and a custom text-based toolbar (built-in EasyMDE toolbar icons require Font Awesome from a CDN, which this app doesn't use). unorderedListStyle is set to "-" to match the app's own template convention. - Add a preview/edit toggle button that swaps the editor for the exact same server-rendered markdown (via /journal/preview) shown after saving, instead of always showing both. - Fix create/edit entry routes to verify the submitted category_id actually belongs to the current user before inserting -- every other write path in this app already checked ownership; this one didn't (found while manually testing the new editor with a typo'd category id that happened to belong to someone else's category, which surfaced as an IntegrityError 500 instead of a clean 404-equivalent). - Fix the default "일상" category template: bare "-" bullet lines don't parse as list items in the markdown renderer (they need a trailing space), and the content_template validator was silently stripping that trailing space off on every save. Backfill migration updates any category still holding the old, broken template text. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -101,6 +101,22 @@ def test_create_entry_rejects_blank_content(auth_client, db_session, test_user):
|
||||
assert journal_service.list_entries(db_session, test_user.id) == []
|
||||
|
||||
|
||||
def test_create_entry_rejects_other_users_category(auth_client, db_session, test_user, other_user):
|
||||
others_category = _make_category(db_session, other_user.id, name="남의 카테고리")
|
||||
|
||||
response = auth_client.post(
|
||||
"/journal/new",
|
||||
data={
|
||||
"category_id": str(others_category.id),
|
||||
"entry_date": date.today().isoformat(),
|
||||
"content": "가로채기 시도",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert "카테고리를 찾을 수 없어요" in response.text
|
||||
assert journal_service.list_entries(db_session, test_user.id) == []
|
||||
|
||||
|
||||
def test_journal_day_detail_shows_entry(auth_client, db_session, test_user):
|
||||
category = _make_category(db_session, test_user.id)
|
||||
today = date.today()
|
||||
@@ -111,12 +127,58 @@ def test_journal_day_detail_shows_entry(auth_client, db_session, test_user):
|
||||
assert "오늘의 기록" in response.text
|
||||
|
||||
|
||||
def test_journal_day_detail_renders_content_as_markdown(auth_client, db_session, test_user):
|
||||
category = _make_category(db_session, test_user.id)
|
||||
today = date.today()
|
||||
_make_entry(db_session, test_user.id, category.id, entry_date=today, content="**굵은 글씨** 테스트")
|
||||
|
||||
response = auth_client.get(f"/journal/day/{today.isoformat()}")
|
||||
assert response.status_code == 200
|
||||
assert "<strong>굵은 글씨</strong>" in response.text
|
||||
|
||||
|
||||
def test_journal_day_detail_strips_script_tags_from_content(auth_client, db_session, test_user):
|
||||
category = _make_category(db_session, test_user.id)
|
||||
today = date.today()
|
||||
_make_entry(
|
||||
db_session, test_user.id, category.id, entry_date=today, content='<script>alert(1)</script>본문'
|
||||
)
|
||||
|
||||
response = auth_client.get(f"/journal/day/{today.isoformat()}")
|
||||
assert response.status_code == 200
|
||||
assert "<script" not in response.text
|
||||
|
||||
|
||||
def test_journal_day_detail_requires_login(client):
|
||||
response = client.get(f"/journal/day/{date.today().isoformat()}", follow_redirects=False)
|
||||
assert response.status_code == 303
|
||||
assert response.headers["location"] == "/login"
|
||||
|
||||
|
||||
def test_preview_renders_markdown(auth_client):
|
||||
response = auth_client.post("/journal/preview", data={"content": "**굵게** 그리고 - 목록"})
|
||||
assert response.status_code == 200
|
||||
assert "<strong>굵게</strong>" in response.text
|
||||
|
||||
|
||||
def test_preview_strips_script_tags(auth_client):
|
||||
response = auth_client.post("/journal/preview", data={"content": '<script>alert(1)</script>본문'})
|
||||
assert response.status_code == 200
|
||||
assert "<script" not in response.text
|
||||
|
||||
|
||||
def test_preview_shows_placeholder_for_blank_content(auth_client):
|
||||
response = auth_client.post("/journal/preview", data={"content": " "})
|
||||
assert response.status_code == 200
|
||||
assert "미리보기가 여기에 표시돼요" in response.text
|
||||
|
||||
|
||||
def test_preview_requires_login(client):
|
||||
response = client.post("/journal/preview", data={"content": "test"}, follow_redirects=False)
|
||||
assert response.status_code == 303
|
||||
assert response.headers["location"] == "/login"
|
||||
|
||||
|
||||
def test_edit_entry_updates_content(auth_client, db_session, test_user):
|
||||
category = _make_category(db_session, test_user.id)
|
||||
entry = _make_entry(db_session, test_user.id, category.id, content="원래 내용")
|
||||
@@ -163,6 +225,27 @@ def test_edit_other_users_entry_returns_404(auth_client, db_session, other_user)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_edit_entry_rejects_moving_to_other_users_category(auth_client, db_session, test_user, other_user):
|
||||
category = _make_category(db_session, test_user.id)
|
||||
entry = _make_entry(db_session, test_user.id, category.id, content="원래 내용")
|
||||
others_category = _make_category(db_session, other_user.id, name="남의 카테고리")
|
||||
|
||||
response = auth_client.post(
|
||||
f"/journal/{entry.id}/edit",
|
||||
data={
|
||||
"category_id": str(others_category.id),
|
||||
"entry_date": entry.entry_date.isoformat(),
|
||||
"content": "가로채기 시도",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert "카테고리를 찾을 수 없어요" in response.text
|
||||
|
||||
db_session.refresh(entry)
|
||||
assert entry.category_id == category.id
|
||||
assert entry.content == "원래 내용"
|
||||
|
||||
|
||||
def test_delete_entry_removes_it(auth_client, db_session, test_user):
|
||||
category = _make_category(db_session, test_user.id)
|
||||
entry = _make_entry(db_session, test_user.id, category.id, content="지울 기록")
|
||||
|
||||
Reference in New Issue
Block a user