journal: multi-select emotions and per-category writing templates
- Mood switches from a single enum column to a many-to-many JournalEntryMood table so an entry can carry several feelings at once; the vocabulary is trimmed to 9 named emotions (dropped the overlapping satisfaction scale) with unified noun-style labels. - Categories can define a content_template that pre-fills the "new entry" textarea when selected (only if the user hasn't started typing), seeded with a Story/Feelings/Decisions/Insights/Actions reflection template on the default "일상" category. - Fixes a real attribute-injection bug found while building the template feature: Jinja's built-in |tojson filter doesn't escape double quotes, which breaks a double-quoted x-data="..." attribute when the JSON payload contains one; added |forceescape and a regression test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -41,12 +41,25 @@ def _upload(filename: str, content_type: str, data: bytes) -> UploadFile:
|
||||
def test_ensure_default_category_creates_once(db_session, test_user):
|
||||
first = journal_service.ensure_default_category(db_session, test_user.id)
|
||||
assert first.name == journal_service.DEFAULT_CATEGORY_NAME
|
||||
assert first.content_template == journal_service.DEFAULT_CATEGORY_TEMPLATE.strip()
|
||||
|
||||
second = journal_service.ensure_default_category(db_session, test_user.id)
|
||||
assert second.id == first.id
|
||||
assert len(journal_service.list_categories(db_session, test_user.id)) == 1
|
||||
|
||||
|
||||
def test_category_content_template_persists_and_updates(db_session, test_user):
|
||||
category = journal_service.create_category(
|
||||
db_session, test_user.id, JournalCategoryCreate(name="회고", content_template="질문 1\n\n질문 2")
|
||||
)
|
||||
assert category.content_template == "질문 1\n\n질문 2"
|
||||
|
||||
updated = journal_service.update_category(
|
||||
db_session, category, JournalCategoryCreate(name="회고", content_template=" ")
|
||||
)
|
||||
assert updated.content_template is None
|
||||
|
||||
|
||||
def test_list_categories_scoped_to_user(db_session, test_user, other_user):
|
||||
_make_category(db_session, test_user.id, name="일상")
|
||||
_make_category(db_session, other_user.id, name="남의 카테고리")
|
||||
|
||||
@@ -8,8 +8,10 @@ from app.schemas.journal import JournalCategoryCreate, JournalEntryCreate
|
||||
from app.services import journal_service
|
||||
|
||||
|
||||
def _make_category(db_session, user_id, name="일상"):
|
||||
return journal_service.create_category(db_session, user_id, JournalCategoryCreate(name=name))
|
||||
def _make_category(db_session, user_id, name="일상", content_template=None):
|
||||
return journal_service.create_category(
|
||||
db_session, user_id, JournalCategoryCreate(name=name, content_template=content_template)
|
||||
)
|
||||
|
||||
|
||||
def _make_entry(db_session, user_id, category_id, entry_date=None, content="기록"):
|
||||
@@ -36,6 +38,29 @@ def test_journal_page_creates_default_category_and_renders(auth_client):
|
||||
assert journal_service.DEFAULT_CATEGORY_NAME in response.text
|
||||
|
||||
|
||||
def test_journal_page_embeds_category_content_template(auth_client):
|
||||
response = auth_client.get("/journal")
|
||||
assert response.status_code == 200
|
||||
assert "Story" in response.text
|
||||
assert "메타인지" in response.text
|
||||
|
||||
|
||||
def test_journal_page_template_with_double_quotes_does_not_break_x_data_attribute(
|
||||
auth_client, db_session, test_user
|
||||
):
|
||||
baseline_form_count = auth_client.get("/journal").text.count("<form")
|
||||
|
||||
_make_category(db_session, test_user.id, name="따옴표카테고리", content_template='내용에 "큰따옴표"가 있어요')
|
||||
|
||||
response = auth_client.get("/journal")
|
||||
assert response.status_code == 200
|
||||
# x-data="..." 속성 안에 이스케이프 안 된 "가 섞이면 속성이 거기서 끊겨 뒤 마크업이 전부
|
||||
# 속성값으로 흡수되는데, 그러면 폼의 <form 태그들이 열린 속성값 텍스트에 파묻혀 개수가
|
||||
# 줄어든다 — 카테고리 하나 늘었다고 <form 개수가 그대로인지로 attribute-injection 여부를 검증한다.
|
||||
assert response.text.count("<form") == baseline_form_count
|
||||
assert "\\"큰따옴표\\"" in response.text
|
||||
|
||||
|
||||
def test_create_entry_via_multipart_form(auth_client, db_session, test_user, tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(journal_service.settings, "journal_media_root", str(tmp_path))
|
||||
category = _make_category(db_session, test_user.id, name="투자")
|
||||
|
||||
Reference in New Issue
Block a user