SciSiteForge/tests/test_scisiteforge.py

348 lines
15 KiB
Python

from __future__ import annotations
import json
import unittest
from pathlib import Path
from types import SimpleNamespace
import sys
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "scripts"))
import build
import translate_site
from scisiteforge.content import SiteContent, cards_from_config, load_citegeist_cards, load_didactopus_cards, load_doclift_cards, load_groundrecall_cards
from scisiteforge.notebook import load_notebooks, render_notebooks
from scisiteforge.themes import get_theme, materialize_theme
from scisiteforge.translations import GenieHiveTranslator, TranslationConfig
class SciSiteForgeTests(unittest.TestCase):
def test_theme_materialization_copies_theme_assets(self) -> None:
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
theme = get_theme("talkorigins-modern")
payload = materialize_theme(theme, tmp_path)
self.assertTrue((tmp_path / "theme" / "style.css").exists())
self.assertTrue((tmp_path / "theme" / "main.js").exists())
self.assertTrue((tmp_path / "theme" / "assets" / "toa.ico").exists())
self.assertTrue((tmp_path / "theme" / "assets" / "toa_logo_001_edit_001.png").exists())
self.assertEqual(payload["theme_name"], "talkorigins-modern")
def test_content_loaders_parse_local_repo_artifacts(self) -> None:
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
doclift_root = tmp_path / "doclift"
doclift_root.mkdir()
(doclift_root / "manifest.json").write_text(
json.dumps(
{
"documents": [
{
"document_id": "doc-1",
"title": "Legacy Document",
"document_kind": "article",
"markdown_path": "documents/doc-1/document.md",
"source_path": "source/doc-1.html",
}
]
}
),
encoding="utf-8",
)
(doclift_root / "documents" / "doc-1").mkdir(parents=True)
(doclift_root / "documents" / "doc-1" / "document.md").write_text("First paragraph.\n\nSecond.", encoding="utf-8")
groundrecall_root = tmp_path / "groundrecall"
groundrecall_root.mkdir()
(groundrecall_root / "groundrecall_query_bundle.json").write_text(
json.dumps(
{
"concept": {"concept_id": "concept::topic", "title": "GroundRecall Topic"},
"summary": "Grounded summary.",
"claims": [{"claim_id": "clm-1", "claim_text": "Claim one", "claim_kind": "summary"}],
}
),
encoding="utf-8",
)
didactopus_root = tmp_path / "didactopus"
didactopus_root.mkdir()
(didactopus_root / "pack.yaml").write_text("name: test-pack\ndisplay_name: Test Pack\n", encoding="utf-8")
(didactopus_root / "concepts.yaml").write_text(
"concepts:\n - id: prior\n title: Prior\n description: Previous knowledge.\n prerequisites: []\n",
encoding="utf-8",
)
citegeist_root = tmp_path / "citegeist"
citegeist_root.mkdir()
(citegeist_root / "refs.bib").write_text(
"""@article{smith2024,\n title = {A Study},\n author = {Smith, Jane and Roe, John},\n year = {2024}\n}\n""",
encoding="utf-8",
)
doclift_cards = load_doclift_cards(doclift_root)
groundrecall_cards = load_groundrecall_cards(groundrecall_root)
didactopus_cards = load_didactopus_cards(didactopus_root)
citegeist_cards = load_citegeist_cards(citegeist_root)
self.assertEqual(doclift_cards[0].title, "Legacy Document")
self.assertEqual(doclift_cards[0].body, "First paragraph.")
self.assertEqual(groundrecall_cards[0].title, "GroundRecall Topic")
self.assertEqual(groundrecall_cards[1].title, "Claim one")
self.assertEqual(didactopus_cards[0].title, "Prior")
self.assertEqual(citegeist_cards[0].title, "A Study")
def test_inline_config_cards_can_seed_example_content(self) -> None:
cards = cards_from_config(
[
{
"title": "Foundation Search",
"body": "Corpus-aware search entry point.",
"href": "/search/",
"meta": "workbench",
"link_label": "Search",
}
],
default_kind="feature",
)
self.assertEqual(cards[0].title, "Foundation Search")
self.assertEqual(cards[0].kind, "feature")
self.assertEqual(cards[0].href, "/search/")
self.assertEqual(cards[0].meta, "workbench")
self.assertEqual(cards[0].link_label, "Search")
def test_build_site_renders_selected_theme_and_content(self) -> None:
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
content_root = tmp_path / "content"
content_root.mkdir()
doclift_root = content_root / "doclift"
doclift_root.mkdir()
(doclift_root / "manifest.json").write_text(
json.dumps({"documents": [{"document_id": "doc-1", "title": "Legacy Document", "markdown_path": "documents/doc-1/document.md"}]}),
encoding="utf-8",
)
(doclift_root / "documents" / "doc-1").mkdir(parents=True)
(doclift_root / "documents" / "doc-1" / "document.md").write_text("Doclift content.", encoding="utf-8")
didactopus_root = content_root / "didactopus"
didactopus_root.mkdir()
(didactopus_root / "pack.yaml").write_text("name: test-pack\ndisplay_name: Test Pack\n", encoding="utf-8")
(didactopus_root / "concepts.yaml").write_text(
"concepts:\n - id: prior\n title: Prior\n description: Previous knowledge.\n prerequisites: []\n",
encoding="utf-8",
)
groundrecall_root = content_root / "groundrecall"
groundrecall_root.mkdir()
(groundrecall_root / "groundrecall_query_bundle.json").write_text(
json.dumps({"concept": {"concept_id": "concept::topic", "title": "GroundRecall Topic"}, "summary": "Grounded summary."}),
encoding="utf-8",
)
citegeist_root = content_root / "citegeist"
citegeist_root.mkdir()
(citegeist_root / "refs.bib").write_text(
"""@article{smith2024,\n title = {A Study},\n author = {Smith, Jane and Roe, John},\n year = {2024}\n}\n""",
encoding="utf-8",
)
config = {
"lang": "en",
"title": "TalkOrigins Preview",
"site_title": "TalkOrigins Archive",
"license": "CC BY-SA 4.0",
"github_url": "https://example.invalid",
"contact_email": "admin@example.invalid",
"theme": "talkorigins-modern",
"languages": [{"code": "en", "name": "English"}],
"navigation": [{"label": "Home", "href": "/"}],
"hero": {
"kicker": "Archive Preview",
"title": "Modernized, reviewable, and still archive-first.",
"lede": "Proof-of-concept output from SciSiteForge.",
"actions": [{"label": "Open", "href": "#overview", "primary": True}],
},
"content_sources": {
"doclift_bundle": str(doclift_root),
"groundrecall_bundle": str(groundrecall_root),
"didactopus_pack": str(didactopus_root),
"bibliography": str(citegeist_root),
},
}
config_path = tmp_path / "site.json"
config_path.write_text(json.dumps(config), encoding="utf-8")
out_dir = tmp_path / "out"
result = build.build_site(config_path, out_dir)
html = (out_dir / "index.html").read_text(encoding="utf-8")
self.assertEqual(result["theme"], "talkorigins-modern")
self.assertIn("TalkOrigins Preview", html)
self.assertIn("Legacy Document", html)
self.assertIn("GroundRecall Topic", html)
self.assertIn("Prior", html)
self.assertIn("A Study", html)
self.assertTrue((out_dir / "theme" / "assets" / "toa.ico").exists())
def test_build_site_filters_languages_by_coverage_and_shows_planned_list(self) -> None:
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
config = {
"lang": "en",
"title": "TalkOrigins Preview",
"site_title": "TalkOrigins Archive",
"license": "CC BY-SA 4.0",
"github_url": "https://example.invalid",
"contact_email": "admin@example.invalid",
"theme": "talkorigins-modern",
"languages": [
{"code": "en", "name": "English", "coverage": True},
{"code": "es", "name": "Español", "coverage": False},
{"code": "fr", "name": "Français", "coverage": False},
],
"language_policy": {
"planned_languages": [
{"code": "es", "name": "Español"},
{"code": "fr", "name": "Français"},
]
},
"navigation": [{"label": "Home", "href": "/"}],
"hero": {
"kicker": "Archive Preview",
"title": "Modernized, reviewable, and still archive-first.",
"lede": "Proof-of-concept output from SciSiteForge.",
"actions": [{"label": "Open", "href": "#overview", "primary": True}],
},
"content_sources": {},
}
config_path = tmp_path / "site.json"
config_path.write_text(json.dumps(config), encoding="utf-8")
out_dir = tmp_path / "out"
build.build_site(config_path, out_dir)
html = (out_dir / "index.html").read_text(encoding="utf-8")
self.assertNotIn('<select id="lang-switch"', html)
self.assertNotIn('value="es"', html)
self.assertNotIn('value="fr"', html)
self.assertIn("Multilingual access", html)
self.assertIn("Target languages under consideration: Español, Français", html)
def test_notebook_pattern_groups_goals_apps_and_source_cards(self) -> None:
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
doclift_root = tmp_path / "doclift"
doclift_root.mkdir()
(doclift_root / "manifest.json").write_text(
json.dumps(
{
"documents": [
{
"document_id": "doc-1",
"title": "Legacy Reading",
"document_kind": "article",
"markdown_path": "documents/doc-1/document.md",
}
]
}
),
encoding="utf-8",
)
(doclift_root / "documents" / "doc-1").mkdir(parents=True)
(doclift_root / "documents" / "doc-1" / "document.md").write_text("Recovered source paragraph.", encoding="utf-8")
config = {
"notebooks": [
{
"id": "digital-evolution",
"title": "Digital Evolution Notebook",
"summary": "Lab plus source-grounded study path.",
"audience": "self-learners",
"goals": ["Connect simulation output to evolutionary concepts"],
"apps": [{"title": "Avida-ED", "href": "/app4/", "description": "Digital evolution lab"}],
"source_kinds": ["notebook"],
}
]
}
notebooks = load_notebooks(config)
content = SiteContent(section_cards=load_doclift_cards(doclift_root))
html = render_notebooks(notebooks, content)
self.assertIn("Digital Evolution Notebook", html)
self.assertIn("Avida-ED", html)
self.assertIn("Legacy Reading", html)
self.assertIn("Recovered source paragraph.", html)
def test_geniehive_translator_uses_openai_compatible_chat_payload(self) -> None:
translator = GenieHiveTranslator(
TranslationConfig(base_url="http://geniehive.local:8800", model="translation-role", api_key="abc123")
)
captured: dict[str, object] = {}
def fake_post_json(path: str, payload: dict) -> dict:
captured["path"] = path
captured["payload"] = payload
return {"choices": [{"message": {"content": "Hola"}}]}
translator._post_json = fake_post_json # type: ignore[method-assign]
result = translator.translate("Hello world", "Spanish", {"evolution": "evolución"})
self.assertEqual(result, "Hola")
self.assertEqual(captured["path"], "/v1/chat/completions")
payload = captured["payload"]
self.assertEqual(payload["model"], "translation-role")
user_text = payload["messages"][1]["content"]
self.assertIn("Spanish", user_text)
self.assertIn("evolución", user_text)
def test_translate_site_builds_translator_from_config(self) -> None:
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
config_path = tmp_path / "site.json"
config_path.write_text(
json.dumps(
{
"translation": {
"base_url": "http://geniehive.local:8800",
"model": "translation-role",
"api_key": "abc123",
"timeout": 33,
"system_prompt": "Translate carefully.",
}
}
),
encoding="utf-8",
)
args = SimpleNamespace(base_url=None, model=None, api_key=None, timeout=None)
translator = translate_site.build_translator(config_path, args)
self.assertEqual(translator.config.provider, "geniehive")
self.assertEqual(translator.config.base_url, "http://geniehive.local:8800")
self.assertEqual(translator.config.model, "translation-role")
self.assertEqual(translator.config.api_key, "abc123")
self.assertEqual(translator.config.timeout, 33)
if __name__ == "__main__":
unittest.main()