← Files NovelistARCHIVED FILE
skills/create-novel-project/tests/test_create_novel_project.py
5.31 KB · Oct 5, 2026 · 18:29 UTC
from __future__ import annotations
import subprocess
import sys
import tempfile
import unittest
import shutil
from datetime import date
from pathlib import Path
SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "create_novel_project.py"
PLUGIN_ROOT = Path(__file__).resolve().parents[3]
class CreateNovelProjectTests(unittest.TestCase):
def run_initializer(self, destination: Path, *arguments: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[
sys.executable,
str(SCRIPT),
"--project-root",
str(destination),
*arguments,
],
check=False,
capture_output=True,
text=True,
)
def test_initializes_configured_project(self) -> None:
with tempfile.TemporaryDirectory() as temporary_directory:
destination = Path(temporary_directory) / "book"
result = self.run_initializer(
destination,
"--title",
"기억의 색인",
"--language",
"ko-KR",
)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
project = (destination / "project.md").read_text(encoding="utf-8")
self.assertIn('title: "기억의 색인"', project)
self.assertIn("language: ko-KR", project)
self.assertIn("# 기억의 색인", project)
self.assertIn(f"created: {date.today().isoformat()}", project)
self.assertTrue((destination / "chapters" / "_template.md").is_file())
self.assertTrue((destination / "published" / ".gitignore").is_file())
self.assertEqual(
(destination / "CLAUDE.md").read_text(encoding="utf-8").strip(),
"@AGENTS.md",
)
self.assertFalse((destination / ".agents" / "skills").exists())
def test_collision_preflight_writes_nothing(self) -> None:
with tempfile.TemporaryDirectory() as temporary_directory:
destination = Path(temporary_directory) / "book"
destination.mkdir()
existing_project = destination / "project.md"
existing_project.write_text("keep me\n", encoding="utf-8")
result = self.run_initializer(destination)
self.assertNotEqual(result.returncode, 0)
self.assertEqual(existing_project.read_text(encoding="utf-8"), "keep me\n")
self.assertFalse((destination / "chapters").exists())
def test_force_rejects_symlink_escape_before_writing(self) -> None:
with tempfile.TemporaryDirectory() as temporary_directory:
temporary_root = Path(temporary_directory)
destination = temporary_root / "book"
outside = temporary_root / "outside"
destination.mkdir()
outside.mkdir()
(destination / "assets").symlink_to(outside, target_is_directory=True)
result = self.run_initializer(destination, "--force")
self.assertNotEqual(result.returncode, 0)
self.assertIn("symbolic link", result.stdout)
self.assertFalse((destination / "project.md").exists())
self.assertEqual(list(outside.iterdir()), [])
def test_rejects_multiline_title_before_writing(self) -> None:
with tempfile.TemporaryDirectory() as temporary_directory:
destination = Path(temporary_directory) / "book"
result = self.run_initializer(destination, "--title", "Line one\nLine two")
self.assertNotEqual(result.returncode, 0)
self.assertFalse(destination.exists())
def test_initializes_from_an_isolated_plugin_copy(self) -> None:
with tempfile.TemporaryDirectory() as temporary_directory:
temporary_root = Path(temporary_directory)
isolated_plugin = temporary_root / "plugin-cache" / "novelist"
shutil.copytree(
PLUGIN_ROOT,
isolated_plugin,
ignore=shutil.ignore_patterns("__pycache__", "*.pyc"),
)
isolated_script = (
isolated_plugin
/ "skills"
/ "create-novel-project"
/ "scripts"
/ "create_novel_project.py"
)
destination = temporary_root / "book"
result = subprocess.run(
[
sys.executable,
str(isolated_script),
"--project-root",
str(destination),
],
check=False,
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertTrue((destination / "project.md").is_file())
self.assertTrue((destination / "CLAUDE.md").is_file())
self.assertFalse((destination / ".agents" / "skills").exists())
def test_rejects_destination_inside_plugin(self) -> None:
destination = PLUGIN_ROOT / "temporary-project-test"
result = self.run_initializer(destination)
self.assertNotEqual(result.returncode, 0)
self.assertIn("plugin installation", result.stdout)
self.assertFalse(destination.exists())
if __name__ == "__main__":
unittest.main()
SHA-256: bd348de276c7676b06c0b3b43578db801247212b960d592c047122f29ed9e509