Files
grateful-journal/backend/tests/test_models.py
2026-03-24 10:48:20 +05:30

197 lines
6.5 KiB
Python

"""Tests for Pydantic data models (backend/models.py)."""
import pytest
from pydantic import ValidationError
from models import (
UserCreate,
UserUpdate,
EncryptionMetadata,
JournalEntryCreate,
JournalEntryUpdate,
MoodEnum,
)
# ---------------------------------------------------------------------------
# UserCreate
# ---------------------------------------------------------------------------
class TestUserCreate:
def test_requires_email(self):
with pytest.raises(ValidationError):
UserCreate()
def test_valid_email_only(self):
user = UserCreate(email="test@example.com")
assert user.email == "test@example.com"
def test_display_name_is_optional(self):
user = UserCreate(email="test@example.com")
assert user.displayName is None
def test_photo_url_is_optional(self):
user = UserCreate(email="test@example.com")
assert user.photoURL is None
def test_all_fields(self):
user = UserCreate(
email="test@example.com",
displayName="Alice",
photoURL="https://example.com/pic.jpg",
)
assert user.displayName == "Alice"
assert user.photoURL == "https://example.com/pic.jpg"
# ---------------------------------------------------------------------------
# UserUpdate
# ---------------------------------------------------------------------------
class TestUserUpdate:
def test_all_fields_optional(self):
update = UserUpdate()
assert update.displayName is None
assert update.photoURL is None
assert update.theme is None
def test_update_only_theme(self):
update = UserUpdate(theme="dark")
assert update.theme == "dark"
assert update.displayName is None
def test_update_only_display_name(self):
update = UserUpdate(displayName="New Name")
assert update.displayName == "New Name"
assert update.theme is None
def test_model_dump_excludes_unset(self):
update = UserUpdate(theme="dark")
dumped = update.model_dump(exclude_unset=True)
assert "theme" in dumped
assert "displayName" not in dumped
# ---------------------------------------------------------------------------
# EncryptionMetadata
# ---------------------------------------------------------------------------
class TestEncryptionMetadata:
def test_requires_ciphertext(self):
with pytest.raises(ValidationError):
EncryptionMetadata(nonce="abc")
def test_requires_nonce(self):
with pytest.raises(ValidationError):
EncryptionMetadata(ciphertext="abc")
def test_requires_both_ciphertext_and_nonce(self):
with pytest.raises(ValidationError):
EncryptionMetadata()
def test_default_algorithm_is_xsalsa20(self):
meta = EncryptionMetadata(ciphertext="abc", nonce="xyz")
assert meta.algorithm == "XSalsa20-Poly1305"
def test_default_encrypted_is_true(self):
meta = EncryptionMetadata(ciphertext="abc", nonce="xyz")
assert meta.encrypted is True
def test_valid_full_metadata(self):
meta = EncryptionMetadata(
encrypted=True,
ciphertext="dGVzdA==",
nonce="bm9uY2U=",
algorithm="XSalsa20-Poly1305",
)
assert meta.ciphertext == "dGVzdA=="
assert meta.nonce == "bm9uY2U="
def test_custom_algorithm_accepted(self):
meta = EncryptionMetadata(ciphertext="abc", nonce="xyz", algorithm="AES-256-GCM")
assert meta.algorithm == "AES-256-GCM"
# ---------------------------------------------------------------------------
# JournalEntryCreate
# ---------------------------------------------------------------------------
class TestJournalEntryCreate:
def test_all_fields_optional(self):
entry = JournalEntryCreate()
assert entry.title is None
assert entry.content is None
assert entry.encryption is None
assert entry.mood is None
def test_encrypted_entry_has_no_plaintext(self):
"""Encrypted entries legitimately have no title or content."""
entry = JournalEntryCreate(
encryption=EncryptionMetadata(ciphertext="abc", nonce="xyz")
)
assert entry.title is None
assert entry.content is None
assert entry.encryption is not None
def test_valid_mood_values(self):
for mood in ("happy", "sad", "neutral", "anxious", "grateful"):
entry = JournalEntryCreate(mood=mood)
assert entry.mood == mood
def test_invalid_mood_raises_validation_error(self):
with pytest.raises(ValidationError):
JournalEntryCreate(mood="ecstatic")
def test_default_is_public_is_false(self):
entry = JournalEntryCreate()
assert entry.isPublic is False
def test_tags_default_is_none(self):
entry = JournalEntryCreate()
assert entry.tags is None
def test_tags_list_accepted(self):
entry = JournalEntryCreate(tags=["family", "work", "health"])
assert entry.tags == ["family", "work", "health"]
# ---------------------------------------------------------------------------
# JournalEntryUpdate
# ---------------------------------------------------------------------------
class TestJournalEntryUpdate:
def test_all_fields_optional(self):
update = JournalEntryUpdate()
assert update.title is None
assert update.mood is None
def test_update_mood_only(self):
update = JournalEntryUpdate(mood="happy")
dumped = update.model_dump(exclude_unset=True)
assert dumped == {"mood": MoodEnum.happy}
def test_invalid_mood_raises_error(self):
with pytest.raises(ValidationError):
JournalEntryUpdate(mood="angry")
def test_update_encryption(self):
update = JournalEntryUpdate(
encryption=EncryptionMetadata(ciphertext="new_ct", nonce="new_nonce")
)
assert update.encryption.ciphertext == "new_ct"
# ---------------------------------------------------------------------------
# MoodEnum
# ---------------------------------------------------------------------------
class TestMoodEnum:
def test_all_enum_values(self):
assert MoodEnum.happy == "happy"
assert MoodEnum.sad == "sad"
assert MoodEnum.neutral == "neutral"
assert MoodEnum.anxious == "anxious"
assert MoodEnum.grateful == "grateful"
def test_enum_used_in_entry_create(self):
entry = JournalEntryCreate(mood=MoodEnum.grateful)
assert entry.mood == "grateful"