Files
sphinx-llms-txt/tests/test_integration.py

381 lines
12 KiB
Python

"""Integration tests for sphinx-llms-txt."""
import sys
from pathlib import Path
from sphinx.testing.util import _clean_up_global_state
def test_build_html_with_llms_txt(basic_sphinx_app):
"""Test building HTML documentation with llms-txt enabled."""
app = basic_sphinx_app
app.build()
# Check if the output file was created
output_file = Path(app.outdir) / "test-llms-full.txt"
assert output_file.exists(), f"Output file {output_file} does not exist"
# Read the content of the output file
content = output_file.read_text()
# Check that content from all pages is included
assert "Welcome to Test Project's documentation!" in content
assert "Page 1 Title" in content
assert "Page 2 Title" in content
assert "Content for section 1" in content
assert "Content for section A" in content
# Check that the include directive has been processed
assert "Page With Include" in content
assert "This is a test page that includes another file:" in content
assert "Changelog" in content # Content from the included file
assert "0.2.0 (2025-01-01)" in content # Content from the included file
assert "0.1.0 (2024-01-01)" in content # Additional content from the included file
assert "This content comes after the include." in content
def test_custom_filename(temp_dir, rootdir):
"""Test using a custom filename for the output."""
from sphinx.testing.util import SphinxTestApp
src_dir = rootdir / "basic"
print(src_dir)
# Create a copy of the configuration with a different filename
custom_conf = src_dir / "conf_custom.py"
with open(src_dir / "conf.py") as f:
conf_content = f.read()
conf_content = conf_content.replace(
'llms_txt_full_filename = "test-llms-full.txt"',
'llms_txt_full_filename = "custom-name.txt"',
)
with open(custom_conf, "w") as f:
f.write(conf_content)
# Create a new test app with the custom configuration
app = SphinxTestApp(
srcdir=src_dir,
builddir=temp_dir,
buildername="html",
freshenv=True,
confoverrides={"llms_txt_full_filename": "custom-name.txt"},
)
app.build()
# Check if the output file with the custom name was created
output_file = Path(app.outdir) / "custom-name.txt"
assert output_file.exists(), f"Output file {output_file} does not exist"
# Custom cleanup to avoid missing_ok issue
import sys
from sphinx.testing.util import _clean_up_global_state
sys.path[:] = app._saved_path
_clean_up_global_state()
# Safe unlink that works with older Python versions
if hasattr(app, "docutils_conf_path") and app.docutils_conf_path.exists():
app.docutils_conf_path.unlink()
def test_max_lines_limit(temp_dir, rootdir):
"""Test that the max lines limit works correctly."""
from sphinx.testing.util import SphinxTestApp
src_dir = rootdir / "basic"
# Create a new test app with a small line limit
app = SphinxTestApp(
srcdir=src_dir,
builddir=temp_dir,
buildername="html",
freshenv=True,
confoverrides={
"llms_txt_full_filename": "limited.txt",
"llms_txt_full_max_size": 10, # Set a small limit to trigger the warning
},
)
app.build()
# Check that the output file was NOT created (since it would exceed the limit)
output_file = Path(app.outdir) / "limited.txt"
assert (
not output_file.exists()
), f"Output file {output_file} exists but should not when limit is exceeded"
# Custom cleanup to avoid missing_ok issue
sys.path[:] = app._saved_path
_clean_up_global_state()
# Safe unlink
if hasattr(app, "docutils_conf_path") and app.docutils_conf_path.exists():
app.docutils_conf_path.unlink()
def test_on_exceed_skip(temp_dir, rootdir):
"""Test that skip action works when size limit is exceeded."""
from sphinx.testing.util import SphinxTestApp
src_dir = rootdir / "basic"
app = SphinxTestApp(
srcdir=src_dir,
builddir=temp_dir,
buildername="html",
freshenv=True,
confoverrides={
"llms_txt_full_filename": "skip-test.txt",
"llms_txt_full_max_size": 20,
"llms_txt_full_size_policy": "warn_skip",
},
)
app.build()
# Check that the output file was NOT created
output_file = Path(app.outdir) / "skip-test.txt"
assert (
not output_file.exists()
), f"Output file {output_file} should not exist with skip action"
# Cleanup
sys.path[:] = app._saved_path
_clean_up_global_state()
if hasattr(app, "docutils_conf_path") and app.docutils_conf_path.exists():
app.docutils_conf_path.unlink()
def test_on_exceed_keep(temp_dir, rootdir):
"""Test that keep action works when size limit is exceeded."""
from sphinx.testing.util import SphinxTestApp
src_dir = rootdir / "basic"
app = SphinxTestApp(
srcdir=src_dir,
builddir=temp_dir,
buildername="html",
freshenv=True,
confoverrides={
"llms_txt_full_filename": "keep-test.txt",
"llms_txt_full_max_size": 20,
"llms_txt_full_size_policy": "info_keep",
},
)
app.build()
# Check that the output file WAS created despite exceeding limit
output_file = Path(app.outdir) / "keep-test.txt"
assert (
output_file.exists()
), f"Output file {output_file} should exist with keep action"
# Verify it has content
content = output_file.read_text()
assert len(content) > 0, "Output file should have content with keep action"
# Cleanup
sys.path[:] = app._saved_path
_clean_up_global_state()
if hasattr(app, "docutils_conf_path") and app.docutils_conf_path.exists():
app.docutils_conf_path.unlink()
def test_on_exceed_note(temp_dir, rootdir):
"""Test that note action works when size limit is exceeded."""
from sphinx.testing.util import SphinxTestApp
src_dir = rootdir / "basic"
app = SphinxTestApp(
srcdir=src_dir,
builddir=temp_dir,
buildername="html",
freshenv=True,
confoverrides={
"llms_txt_full_filename": "note-test.txt",
"llms_txt_full_max_size": 20,
"llms_txt_full_size_policy": "warn_note",
},
)
app.build()
# Check that the output file WAS created with placeholder content
output_file = Path(app.outdir) / "note-test.txt"
assert (
output_file.exists()
), f"Output file {output_file} should exist with note action"
# Verify it has the placeholder content
content = output_file.read_text()
assert (
"This file was not generated because it exceeded the configured size limit."
in content
)
assert "llms_txt_full_max_size" in content
assert "llms_txt_full_size_policy" in content
assert "Configured max size: 20 lines" in content
# Cleanup
sys.path[:] = app._saved_path
_clean_up_global_state()
if hasattr(app, "docutils_conf_path") and app.docutils_conf_path.exists():
app.docutils_conf_path.unlink()
def test_on_exceed_invalid_config(temp_dir, rootdir):
"""Test behavior with invalid configuration values."""
from sphinx.testing.util import SphinxTestApp
src_dir = rootdir / "basic"
app = SphinxTestApp(
srcdir=src_dir,
builddir=temp_dir,
buildername="html",
freshenv=True,
confoverrides={
"llms_txt_full_filename": "invalid-test.txt",
"llms_txt_full_max_size": 20,
"llms_txt_full_size_policy": "invalid_format", # Invalid config
},
)
app.build()
# Should fall back to default behavior (warn_skip)
output_file = Path(app.outdir) / "invalid-test.txt"
assert (
not output_file.exists()
), f"Output file {output_file} should not exist with invalid config fallback"
# Cleanup
sys.path[:] = app._saved_path
_clean_up_global_state()
if hasattr(app, "docutils_conf_path") and app.docutils_conf_path.exists():
app.docutils_conf_path.unlink()
def test_title_override(temp_dir, rootdir):
"""Test that the title override works correctly."""
from sphinx.testing.util import SphinxTestApp
src_dir = rootdir / "basic"
# Custom title to override the default project name
custom_title = "Custom Title Override"
# Create a new test app with the title override
app = SphinxTestApp(
srcdir=src_dir,
builddir=temp_dir,
buildername="html",
freshenv=True,
confoverrides={
"llms_txt_title": custom_title,
},
)
app.build()
# Check if the summary file was created
summary_file = Path(app.outdir) / "llms.txt"
assert summary_file.exists(), f"Summary file {summary_file} does not exist"
# Read the content of the summary file
content = summary_file.read_text()
# Check that the custom title was used
assert (
f"# {custom_title}" in content
), f"Custom title '{custom_title}' not found in summary file"
# Ensure the default project name was NOT used
assert (
"# Test Project" not in content
), "Default project name was used instead of custom title"
# Custom cleanup to avoid missing_ok issue
sys.path[:] = app._saved_path
_clean_up_global_state()
# Safe unlink
if hasattr(app, "docutils_conf_path") and app.docutils_conf_path.exists():
app.docutils_conf_path.unlink()
def test_exclusion(temp_dir, rootdir):
"""Test that the exclude patterns work correctly."""
from sphinx.testing.util import SphinxTestApp
src_dir = rootdir / "basic"
# Create a new test app with exclude patterns
app = SphinxTestApp(
srcdir=src_dir,
builddir=temp_dir,
buildername="html",
freshenv=True,
confoverrides={
"llms_txt_full_filename": "excluded.txt",
"llms_txt_exclude": [
"page1",
"page_with_*",
], # Exclude page1 and any page starting with page_with_
},
)
app.build()
# Check if the output file was created
output_file = Path(app.outdir) / "excluded.txt"
assert output_file.exists(), f"Output file {output_file} does not exist"
# Read the content of the output file
content = output_file.read_text()
# Check that index and page2 content is included
assert (
"Welcome to Test Project's documentation!" in content
) # Index should be included
assert "Page 2 Title" in content # page2 title should be included
assert "Content for section A" in content # Content from page2 should be included
# Check that excluded content is NOT included
assert "Page 1 Title" not in content # page1 title should be excluded
assert (
"Content for section 1" not in content
) # Content from page1 should be excluded
assert (
"Page With Include" not in content
) # page_with_include title should be excluded
# Extra debug info for test
print(f"\nContent snippet: {content[:500]}...\n")
# Check that none of the content from page1 appears
page1_phrases = [
"Page 1 Title",
"This is the content of page 1",
"Section 1",
"Content for section 1",
"Section 2",
"Content for section 2",
]
for phrase in page1_phrases:
assert phrase not in content, f"Found excluded content: '{phrase}'"
# Custom cleanup to avoid missing_ok issue
sys.path[:] = app._saved_path
_clean_up_global_state()
# Safe unlink
if hasattr(app, "docutils_conf_path") and app.docutils_conf_path.exists():
app.docutils_conf_path.unlink()