Files
stack/tests/docs/test_docs_build.py
kert ea0ead0d8c fix: skip library.json test when bib.sqlite has no data (fixes CI)
The test assumed bib.sqlite always has items when present. In CI,
a stale or empty bib.sqlite could exist without data, causing the
export to produce an empty library.json. Now validates the database
has items before asserting on the export output.
2026-03-26 02:01:31 -04:00

157 lines
5.3 KiB
Python

"""Integration tests for the docs build pipeline."""
from __future__ import annotations
import json
import re
import subprocess
import sys
from pathlib import Path
import pytest
_ROOT = Path(__file__).resolve().parents[2]
BIB_DB = _ROOT / "data/bib.sqlite"
EXPORT_SCRIPT = _ROOT / "docs/scripts/export_library.py"
LIBRARY_JSON = _ROOT / "docs/static/library.json"
DOCS_DIR = _ROOT / "docs/docs"
INTRO_MD = DOCS_DIR / "intro.md"
class TestExportLibrary:
"""Tests for docs/scripts/export_library.py."""
def test_export_runs_without_error(self):
result = subprocess.run(
[sys.executable, str(EXPORT_SCRIPT)],
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stderr
def test_library_json_has_items(self):
"""library.json should have >0 items when bib.sqlite has data."""
if not BIB_DB.exists():
pytest.skip("bib.sqlite not available")
# Verify bib.sqlite is a real database with items
import sqlite3
try:
con = sqlite3.connect(str(BIB_DB))
count = con.execute("SELECT count(*) FROM items").fetchone()[0]
con.close()
except (sqlite3.OperationalError, sqlite3.DatabaseError):
pytest.skip("bib.sqlite is not a valid database")
if count == 0:
pytest.skip("bib.sqlite has no items")
# Run the export
subprocess.run([sys.executable, str(EXPORT_SCRIPT)], check=True)
data = json.loads(LIBRARY_JSON.read_text())
assert len(data["items"]) > 0, "library.json has no items"
assert len(data["tags"]) > 0, "library.json has no tags"
def test_item_has_required_fields(self):
"""Each item must have all required fields."""
if not LIBRARY_JSON.exists():
import pytest
pytest.skip("library.json not generated")
data = json.loads(LIBRARY_JSON.read_text())
if not data["items"]:
import pytest
pytest.skip("no items in library.json")
required = {
"key",
"item_type",
"title",
"url",
"abstract",
"tags",
"collections",
"creators",
}
for item in data["items"][:10]: # spot check first 10
missing = required - set(item.keys())
assert not missing, f"Item {item.get('key', '?')} missing fields: {missing}"
def test_abstracts_not_truncated(self):
"""Abstracts should not be truncated to 300 chars."""
if not LIBRARY_JSON.exists():
import pytest
pytest.skip("library.json not generated")
data = json.loads(LIBRARY_JSON.read_text())
# Find items with long abstracts
truncated = [
i["key"]
for i in data["items"]
if i["abstract"].endswith("...") and len(i["abstract"]) == 300
]
assert not truncated, f"Found truncated abstracts: {truncated[:5]}"
def test_tags_in_items_match_tag_list(self):
"""Every tag referenced by items should appear in the tags list."""
if not LIBRARY_JSON.exists():
import pytest
pytest.skip("library.json not generated")
data = json.loads(LIBRARY_JSON.read_text())
tag_names = {t["name"] for t in data["tags"]}
item_tags = set()
for item in data["items"]:
item_tags.update(item["tags"])
orphans = item_tags - tag_names
assert not orphans, f"Tags on items but not in tags list: {orphans}"
class TestIntroLinks:
"""Verify intro.md links resolve to real pages."""
def test_intro_exists(self):
assert INTRO_MD.exists(), "intro.md not found"
def test_no_broken_relative_links(self):
"""All relative links in intro.md should point to existing files."""
if not INTRO_MD.exists():
import pytest
pytest.skip("intro.md not found")
content = INTRO_MD.read_text()
# Find markdown links like [text](path)
links = re.findall(r"\[.*?\]\(([^)]+)\)", content)
for link in links:
if link.startswith("http") or link.startswith("#"):
continue
# Relative link — check it resolves
target = DOCS_DIR / link.lstrip("/")
# Could be a directory with index.md or a .md file
exists = (
target.exists()
or target.with_suffix(".md").exists()
or (target / "index.md").exists()
or target.parent.exists() # directory exists
)
assert exists, f"Broken link in intro.md: {link}"
class TestExtractDocs:
"""Verify the API doc extraction produces output."""
def test_api_docs_directory_exists(self):
api_dir = DOCS_DIR / "api"
if not api_dir.exists():
import pytest
pytest.skip("API docs not generated yet")
assert api_dir.is_dir()
def test_api_has_packages(self):
api_dir = DOCS_DIR / "api"
if not api_dir.exists():
pytest.skip("API docs not generated yet")
packages = [d.name for d in api_dir.iterdir() if d.is_dir()]
if not packages:
pytest.skip("API docs not generated yet (griffe not run)")
assert len(packages) > 0