Files
talemate/tests/test_files.py
veguAI f5d41c04c8 0.37.0 (#267)
0.37.0

- **Director Planning** — Multi-step todo lists in director chat plus a Generate long progress action for multi-beat scene arcs.
- **Auto Narration** — Unified auto-narration replacing the old Narrate after Dialogue toggle, with a chance slider and weighted action mix.
- **LLM Prompt Templates Manager** — Dedicated UI tab for viewing, creating, editing, and deleting prompt templates.
- **Character Folders** — Collapsible folders in the World Editor character list, synced across linked scenes.
- **OpenAI Compatible TTS** — Connect any number of OpenAI-compatible TTS servers in parallel.
- **KoboldCpp TTS Auto-Setup** — KoboldCpp clients with a TTS model loaded register themselves as a TTS backend.
- **Model Testing Harness** — Bundled scene that runs basic capability tests against any connected LLM.

Plus 27 improvements and 28 bug fixes
2026-05-12 21:01:51 +03:00

150 lines
6.2 KiB
Python

"""Unit tests for talemate.files (scene directory listing)."""
import os
import pytest
from talemate.files import list_scenes_directory, _list_files_and_directories
# ---------------------------------------------------------------------------
# Fixtures: build a fake scenes/ tree we can assert against deterministically
# ---------------------------------------------------------------------------
@pytest.fixture
def scene_tree(tmp_path):
"""Construct a 'scenes/' directory mirroring the real talemate layout.
The real layout has scene JSONs at scenes/<scene>/*.json and shared
character images at scenes/characters/*.png|webp. The helper's fnmatch
patterns ('characters/*.png' and '*/*.json') only match these top-level
locations relative to the scenes root.
"""
scenes_root = tmp_path / "scenes"
scenes_root.mkdir(parents=True)
files = {
# Scene JSONs (one folder per scene)
"alice_in_wonderland/alice.json": '{"a": 1}',
"bob_adventure/bob.json": '{"b": 1}',
# Top-level character images
"characters/alice.png": b"\x89PNG",
"characters/queen.webp": b"WEBP",
# Files that must be filtered out:
# - non-image extension in characters/
"characters/notes.txt": "ignored extension",
# - JSON inside a 'nodes' directory must be skipped
"alice_in_wonderland/nodes/scene-loop.json": '{"node": true}',
# - changelog directory must be skipped
"alice_in_wonderland/changelog/0001.json": '{"x": true}',
# - assets directory must be skipped
"assets/portrait.png": b"\x89PNG",
"assets/library.json": '{"asset": true}',
}
paths = {}
for rel, content in files.items():
full = scenes_root / rel
full.parent.mkdir(parents=True, exist_ok=True)
mode = "wb" if isinstance(content, bytes) else "w"
with open(full, mode) as fh:
fh.write(content)
paths[rel] = str(full)
return {"root": str(scenes_root), "tmp": str(tmp_path), "paths": paths}
# ---------------------------------------------------------------------------
# _list_files_and_directories: the underlying helper
# ---------------------------------------------------------------------------
class TestListFilesAndDirectories:
def test_lists_images_and_jsons_when_images_enabled(self, scene_tree):
result = _list_files_and_directories(scene_tree["root"], ".", list_images=True)
# Convert to set for ordering-independent comparison
result_set = set(result)
expected = {
scene_tree["paths"]["characters/alice.png"],
scene_tree["paths"]["characters/queen.webp"],
scene_tree["paths"]["alice_in_wonderland/alice.json"],
scene_tree["paths"]["bob_adventure/bob.json"],
}
assert result_set == expected
def test_omits_images_when_list_images_false(self, scene_tree):
result = _list_files_and_directories(scene_tree["root"], ".", list_images=False)
# Only json files (not in nodes/changelog/assets) should appear
for path in result:
assert path.endswith(".json")
result_set = set(result)
assert result_set == {
scene_tree["paths"]["alice_in_wonderland/alice.json"],
scene_tree["paths"]["bob_adventure/bob.json"],
}
def test_skips_json_inside_nodes_directory(self, scene_tree):
result = _list_files_and_directories(scene_tree["root"], ".", list_images=True)
# nodes/scene-loop.json must NOT appear
for path in result:
assert os.sep + "nodes" + os.sep not in path
def test_skips_changelog_directory(self, scene_tree):
result = _list_files_and_directories(scene_tree["root"], ".", list_images=True)
for path in result:
assert os.sep + "changelog" + os.sep not in path
def test_skips_assets_directory(self, scene_tree):
result = _list_files_and_directories(scene_tree["root"], ".", list_images=True)
for path in result:
assert os.sep + "assets" + os.sep not in path
def test_ignores_unsupported_extensions(self, scene_tree):
result = _list_files_and_directories(scene_tree["root"], ".", list_images=True)
# The 'characters/notes.txt' file should be filtered out
for path in result:
assert not path.endswith(".txt")
def test_returns_empty_list_for_empty_root(self, tmp_path):
empty_dir = tmp_path / "empty_scenes"
empty_dir.mkdir()
result = _list_files_and_directories(str(empty_dir), ".", list_images=True)
assert result == []
def test_returns_empty_for_nonexistent_root(self, tmp_path):
# os.walk silently returns nothing for missing roots; the helper
# must follow that behaviour rather than raising.
result = _list_files_and_directories(
str(tmp_path / "does_not_exist"), ".", list_images=True
)
assert result == []
# ---------------------------------------------------------------------------
# list_scenes_directory: thin wrapper around the helper
# ---------------------------------------------------------------------------
class TestListScenesDirectory:
def test_walks_scenes_subdir_of_cwd(self, scene_tree, monkeypatch):
# list_scenes_directory uses os.getcwd() / "scenes" as the root
monkeypatch.chdir(scene_tree["tmp"])
result = list_scenes_directory(list_images=True)
# The fake scenes tree has 4 matching files (2 JSONs + 2 images)
assert len(result) == 4
result_set = set(result)
assert scene_tree["paths"]["characters/alice.png"] in result_set
assert scene_tree["paths"]["alice_in_wonderland/alice.json"] in result_set
def test_passes_list_images_flag_through(self, scene_tree, monkeypatch):
monkeypatch.chdir(scene_tree["tmp"])
json_only = list_scenes_directory(list_images=False)
assert all(path.endswith(".json") for path in json_only)
def test_returns_empty_when_no_scenes_dir(self, tmp_path, monkeypatch):
monkeypatch.chdir(str(tmp_path))
# No scenes/ directory exists -> empty list (os.walk on missing path)
assert list_scenes_directory(list_images=True) == []