refactor: rewrite from Ruby/Sinatra to Python/FastAPI
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"""Tests for Store — post loading, saving, deleting, media handling."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from volumen.post import Post
|
||||
from volumen.store import Store
|
||||
|
||||
|
||||
class TestLoading:
|
||||
def test_all_loads_every_post(self, store: Store) -> None:
|
||||
posts = store.all()
|
||||
assert len(posts) == 2 # hello.md + draft.md
|
||||
|
||||
def test_slug_falls_back_to_filename(self, store: Store) -> None:
|
||||
slugs = sorted(p.slug for p in store.all())
|
||||
assert slugs == ["draft", "hello"]
|
||||
|
||||
def test_lang_derived_from_subdirectory(self, tmp_path: Path) -> None:
|
||||
content = tmp_path / "posts"
|
||||
content.mkdir()
|
||||
(content / "hello.md").write_text('+++\ntitle = "Hello"\n+++\n\nBody\n')
|
||||
cs_dir = content / "cs"
|
||||
cs_dir.mkdir()
|
||||
(cs_dir / "ahoj.md").write_text('+++\ntitle = "Ahoj"\n+++\n\nTelo\n')
|
||||
st = Store(str(content), default_lang="en")
|
||||
assert st.find("ahoj").lang == "cs"
|
||||
assert st.find("hello").lang == "en"
|
||||
|
||||
def test_find_returns_none_when_missing(self, store: Store) -> None:
|
||||
assert store.find("nope") is None
|
||||
|
||||
def test_all_langs_post_visible_in_any_language(self, tmp_content_dir: Path) -> None:
|
||||
(tmp_content_dir / "global.md").write_text(
|
||||
'+++\ntitle = "Global"\nlang = "en"\nall_langs = true\n+++\n\nVisible everywhere.\n'
|
||||
)
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
assert store.find("global", lang="cs") is not None
|
||||
assert store.find("global", lang="en") is not None
|
||||
|
||||
def test_draft_post_is_loaded(self, store: Store) -> None:
|
||||
draft = store.find("draft")
|
||||
assert draft is not None
|
||||
assert draft.draft is True
|
||||
|
||||
def test_post_with_missing_slug_derived_from_path(self, tmp_content_dir: Path) -> None:
|
||||
(tmp_content_dir / "noslug.md").write_text('+++\ntitle = "No Slug"\n+++\n\nBody\n')
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
post = store.find("noslug")
|
||||
assert post is not None
|
||||
assert post.slug == "noslug"
|
||||
assert post.lang == "en"
|
||||
|
||||
|
||||
class TestSave:
|
||||
def test_save_new_post(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
post = Post(metadata={"slug": "newpost", "title": "New"}, body="Content")
|
||||
store.save(post)
|
||||
assert os.path.isfile(os.path.join(str(tmp_content_dir), "newpost.md"))
|
||||
|
||||
def test_save_updates_existing_post(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
post = store.find("hello")
|
||||
assert post is not None
|
||||
post.body = "Updated content."
|
||||
store.save(post)
|
||||
reloaded = store.find("hello")
|
||||
assert reloaded is not None
|
||||
assert "Updated content." in reloaded.body
|
||||
|
||||
def test_save_rejects_path_traversal_slug(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir))
|
||||
post = Post(metadata={"slug": "../../etc/passwd"}, body="x")
|
||||
with pytest.raises(ValueError, match="slug escapes"):
|
||||
store.save(post)
|
||||
|
||||
def test_save_uses_lang_subdirectory(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
post = Post(metadata={"slug": "bonjour", "lang": "fr"}, body="x")
|
||||
store.save(post)
|
||||
assert os.path.isfile(os.path.join(str(tmp_content_dir), "fr", "bonjour.md"))
|
||||
|
||||
def test_save_uses_root_for_default_language(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
post = Post(metadata={"slug": "hello-new", "lang": "en"}, body="x")
|
||||
store.save(post)
|
||||
assert os.path.isfile(os.path.join(str(tmp_content_dir), "hello-new.md"))
|
||||
assert not os.path.isfile(os.path.join(str(tmp_content_dir), "en", "hello-new.md"))
|
||||
|
||||
|
||||
class TestDelete:
|
||||
def test_delete_post(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
path = os.path.join(str(tmp_content_dir), "hello.md")
|
||||
assert os.path.isfile(path)
|
||||
store.delete("hello")
|
||||
assert not os.path.isfile(path)
|
||||
|
||||
|
||||
class TestMedia:
|
||||
def test_media_file_path_traversal_prevention(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir))
|
||||
bad = store.media_file("../etc/passwd")
|
||||
assert bad is None
|
||||
|
||||
def test_media_file_nonexistent(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir))
|
||||
assert store.media_file("nonexistent.webp") is None
|
||||
|
||||
|
||||
class TestCacheInvalidation:
|
||||
def test_mtime_cache_invalidation(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
initial = len(store.all())
|
||||
|
||||
# Add a new file
|
||||
(tmp_content_dir / "new.md").write_text('+++\ntitle = "New"\n+++\n\nBody\n')
|
||||
assert len(store.all()) == initial + 1
|
||||
|
||||
def test_cache_invalidation_on_save(self, tmp_content_dir: Path) -> None:
|
||||
store = Store(str(tmp_content_dir), default_lang="en")
|
||||
initial = len(store.all())
|
||||
post = Post(metadata={"slug": "extra"}, body="x")
|
||||
store.save(post)
|
||||
assert len(store.all()) == initial + 1
|
||||
Reference in New Issue
Block a user