203 lines
6.1 KiB
Python
203 lines
6.1 KiB
Python
"""Tests for bulk book import script."""
|
|
|
|
import tempfile
|
|
import tomllib
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from scripts.import_books import (
|
|
extract_alias_words,
|
|
generate_aliases,
|
|
generate_slug,
|
|
parse_txt_file,
|
|
txt_to_toml,
|
|
)
|
|
|
|
|
|
def test_parse_txt_file_with_title_and_content():
|
|
"""Parse a basic .txt file with title and content."""
|
|
content = "The Frog King\n\nOnce upon a time...\nThere was a princess."
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
|
f.write(content)
|
|
f.flush()
|
|
|
|
title, text = parse_txt_file(Path(f.name))
|
|
|
|
assert title == "The Frog King"
|
|
assert text == "Once upon a time...\nThere was a princess."
|
|
|
|
|
|
def test_parse_txt_file_empty_content():
|
|
"""Parse file with only title and blank line."""
|
|
content = "Title Only\n\n"
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
|
f.write(content)
|
|
f.flush()
|
|
|
|
title, text = parse_txt_file(Path(f.name))
|
|
|
|
assert title == "Title Only"
|
|
assert text == ""
|
|
|
|
|
|
def test_parse_txt_file_no_blank_line():
|
|
"""Parse file where second line is not blank."""
|
|
content = "Title\nImmediate content"
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
|
f.write(content)
|
|
f.flush()
|
|
|
|
with pytest.raises(ValueError, match="Expected blank line"):
|
|
parse_txt_file(Path(f.name))
|
|
|
|
|
|
def test_generate_slug_from_filename():
|
|
"""Convert filename to slug for TOML output."""
|
|
result = generate_slug("001_the_frog_king_or_iron_henry.txt")
|
|
assert result == "001_the_frog_king_or_iron_henry"
|
|
assert generate_slug("002_cat_and_mouse.txt") == "002_cat_and_mouse"
|
|
assert generate_slug("simple.txt") == "simple"
|
|
|
|
|
|
def test_extract_alias_words():
|
|
"""Extract meaningful words from title for aliases."""
|
|
assert extract_alias_words("The Frog King") == ["frog", "king"]
|
|
result = extract_alias_words("Cat and Mouse in Partnership")
|
|
assert result == ["cat", "mouse", "partnership"]
|
|
result = extract_alias_words("The Frog-King, or Iron Henry")
|
|
assert result == ["frog-king", "iron", "henry"]
|
|
assert extract_alias_words("Our Lady's Child") == ["lady's", "child"]
|
|
|
|
|
|
def test_generate_aliases():
|
|
"""Generate aliases from title."""
|
|
# Basic title
|
|
aliases = generate_aliases("The Frog King")
|
|
assert "frog king" in aliases
|
|
assert "frog" in aliases
|
|
assert "king" in aliases
|
|
|
|
# With punctuation - gets full phrase plus individual words
|
|
aliases = generate_aliases("The Frog-King, or Iron Henry")
|
|
assert "frog-king iron henry" in aliases
|
|
assert "frog-king" in aliases
|
|
assert "iron" in aliases
|
|
assert "henry" in aliases
|
|
|
|
# Single word title should not generate meaningless aliases
|
|
aliases = generate_aliases("Single")
|
|
assert aliases == ["single"]
|
|
|
|
|
|
def test_txt_to_toml_basic():
|
|
"""Generate valid TOML from title and text."""
|
|
title = "The Frog King"
|
|
text = "Once upon a time..."
|
|
|
|
toml_str = txt_to_toml(title, text)
|
|
|
|
# Parse the generated TOML to verify it's valid
|
|
data = tomllib.loads(toml_str)
|
|
|
|
assert data["name"] == "The Frog King"
|
|
assert data["description"] == "a leather-bound story book"
|
|
assert data["portable"] is True
|
|
assert "frog king" in data["aliases"]
|
|
assert data["readable_text"] == "Once upon a time..."
|
|
|
|
|
|
def test_txt_to_toml_multiline_text():
|
|
"""Generate TOML with multiline readable_text."""
|
|
title = "Test Story"
|
|
text = "Line 1\nLine 2\nLine 3"
|
|
|
|
toml_str = txt_to_toml(title, text)
|
|
data = tomllib.loads(toml_str)
|
|
|
|
assert data["readable_text"] == "Line 1\nLine 2\nLine 3"
|
|
|
|
|
|
def test_txt_to_toml_empty_text():
|
|
"""Generate TOML with empty readable_text."""
|
|
title = "Empty Story"
|
|
text = ""
|
|
|
|
toml_str = txt_to_toml(title, text)
|
|
data = tomllib.loads(toml_str)
|
|
|
|
assert data["readable_text"] == ""
|
|
|
|
|
|
def test_full_pipeline_single_file(tmp_path):
|
|
"""Test complete pipeline from .txt to .toml."""
|
|
from scripts.import_books import import_books
|
|
|
|
# Create input directory with one file
|
|
input_dir = tmp_path / "input"
|
|
input_dir.mkdir()
|
|
|
|
txt_file = input_dir / "001_the_frog_king.txt"
|
|
txt_file.write_text("The Frog King\n\nOnce upon a time...")
|
|
|
|
# Create output directory
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
# Run import
|
|
import_books(input_dir, output_dir)
|
|
|
|
# Verify output file was created
|
|
toml_file = output_dir / "001_the_frog_king.toml"
|
|
assert toml_file.exists()
|
|
|
|
# Verify contents
|
|
with open(toml_file, "rb") as f:
|
|
data = tomllib.load(f)
|
|
|
|
assert data["name"] == "The Frog King"
|
|
assert data["readable_text"] == "Once upon a time..."
|
|
|
|
|
|
def test_full_pipeline_multiple_files(tmp_path):
|
|
"""Test pipeline with multiple files."""
|
|
from scripts.import_books import import_books
|
|
|
|
input_dir = tmp_path / "input"
|
|
input_dir.mkdir()
|
|
|
|
# Create multiple files
|
|
(input_dir / "001_story_one.txt").write_text("Story One\n\nText one")
|
|
(input_dir / "002_story_two.txt").write_text("Story Two\n\nText two")
|
|
(input_dir / "003_story_three.txt").write_text("Story Three\n\nText three")
|
|
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
import_books(input_dir, output_dir)
|
|
|
|
# Verify all files were created
|
|
assert (output_dir / "001_story_one.toml").exists()
|
|
assert (output_dir / "002_story_two.toml").exists()
|
|
assert (output_dir / "003_story_three.toml").exists()
|
|
|
|
|
|
def test_full_pipeline_skips_non_txt(tmp_path):
|
|
"""Pipeline should only process .txt files."""
|
|
from scripts.import_books import import_books
|
|
|
|
input_dir = tmp_path / "input"
|
|
input_dir.mkdir()
|
|
|
|
(input_dir / "story.txt").write_text("Title\n\nContent")
|
|
(input_dir / "README.md").write_text("# Not a story")
|
|
(input_dir / "data.json").write_text("{}")
|
|
|
|
output_dir = tmp_path / "output"
|
|
output_dir.mkdir()
|
|
|
|
import_books(input_dir, output_dir)
|
|
|
|
# Only the .txt file should generate output
|
|
assert (output_dir / "story.toml").exists()
|
|
assert not (output_dir / "README.toml").exists()
|
|
assert not (output_dir / "data.toml").exists()
|