Files

131 lines
5.2 KiB
Python
Raw Permalink Normal View History

"""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