Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions tests/test_build_explainers.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,53 @@ def test_render_markdown_offsets_heading_levels_by_one():
assert '<h6 id="deep">Deep</h6>' in html # h6 + 1 stays h6, not h7


def test_render_markdown_flushes_lists_quotes_rules_and_code():
script = importlib.import_module("scripts.build_explainers")
source = (
"Paragraph before a list.\n\n"
"- first item\n- second item\n\n"
"1. numbered item\n\n"
"> quoted line\n\n"
"---\n\n"
"```python\nprint('ok')\n```\n"
)

html = script.render_markdown(source, set())

assert "<p>Paragraph before a list.</p>" in html
assert "<ul><li>first item</li><li>second item</li></ul>" in html
assert "<ol><li>numbered item</li></ol>" in html
assert "<blockquote><p>quoted line</p></blockquote>" in html
assert "<hr>" in html
assert '<pre><code class="language-python">print(&#x27;ok&#x27;)</code></pre>' in html


def test_resolve_link_target_handles_unknown_markdown_and_project_paths():
script = importlib.import_module("scripts.build_explainers")

assert script.resolve_link_target("unknown-topic.md?view=full#intro", {"known-topic"}) == "unknown-topic.html?view=full#intro"
assert script.resolve_link_target("../Benefits%20Denial/", set()) == "../index.html#project-benefits"
assert script.resolve_link_target("../notebooks/example.ipynb", set()) == "../notebooks/example.ipynb"


def test_faq_skips_nonparagraph_content_and_truncates_long_why_answer():
script = importlib.import_module("scripts.build_explainers")
why = "First sentence. " + ("additional context " * 40)
markdown = (
"## Definition\n\n| Term | Meaning |\n| --- | --- |\n\n"
"A definition paragraph.\n\n"
"## Why It Matters\n\n- a list item\n\n"
f"{why}\n\n## Example\n\nLater text.\n"
)

pairs = script._faq(_entry("sample", "Sample Topic"), markdown)

assert pairs[0] == ("What is Sample Topic?", "A definition paragraph.")
assert pairs[1][0] == "Why does this matter for fairness?"
assert len(pairs[1][1]) <= 501
assert pairs[1][1].startswith("First sentence.")


def test_seo_title_uses_the_short_title_and_rejects_a_long_one():
# #1105: a <title> over 65 characters is cut in search results.
script = importlib.import_module("scripts.build_explainers")
Expand Down Expand Up @@ -349,6 +396,15 @@ def test_the_build_refuses_a_missing_or_empty_og_image(site):
script.main()


def test_the_build_rejects_a_data_entry_without_a_slug(site):
script, root, entries = site
del entries[0]["slug"]
(root / "assets" / "explainers-data.json").write_text(json.dumps(entries), encoding="utf-8")

with pytest.raises(KeyError, match="slug"):
script.main()


def test_the_mirror_drops_a_stale_explainer(site):
script, root, _ = site
stale = root / "faircode" / "_explainers" / "removed-topic.md"
Expand Down
45 changes: 45 additions & 0 deletions tests/test_check_issue_refs.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,51 @@ def test_the_closes_of_a_pull_request_come_from_the_event_payload(tmp_path):
assert script.pull_request_closes({"GITHUB_EVENT_NAME": "pull_request", "GITHUB_EVENT_PATH": str(tmp_path / "missing.json")}) == set()


def test_a_malformed_pull_request_event_is_ignored(tmp_path):
payload = tmp_path / "event.json"
payload.write_text("not json", encoding="utf-8")
assert script.pull_request_closes({"GITHUB_EVENT_NAME": "pull_request", "GITHUB_EVENT_PATH": str(payload)}) == set()


def test_load_states_skips_when_gh_is_missing_or_the_issue_query_fails(monkeypatch):
monkeypatch.setattr(script.shutil, "which", lambda _name: None)
assert script.load_states(None) is None

class Failed:
returncode = 1

monkeypatch.setattr(script.shutil, "which", lambda _name: "/usr/bin/gh")
monkeypatch.setattr(script.subprocess, "run", lambda *args, **kwargs: Failed())
assert script.load_states(None) is None


def test_load_states_merges_pull_requests_when_available(monkeypatch):
class Result:
def __init__(self, returncode, stdout):
self.returncode = returncode
self.stdout = stdout

results = iter((
Result(0, '[{"number": 1, "state": "OPEN"}]'),
Result(0, '[{"number": 2, "state": "MERGED"}]'),
))
monkeypatch.setattr(script.shutil, "which", lambda _name: "/usr/bin/gh")
monkeypatch.setattr(script.subprocess, "run", lambda *args, **kwargs: next(results))
assert script.load_states(None) == {1: "open", 2: "merged"}


def test_load_states_keeps_issue_states_when_the_pull_request_query_fails(monkeypatch):
class Result:
def __init__(self, returncode, stdout):
self.returncode = returncode
self.stdout = stdout

results = iter((Result(0, '[{"number": 1, "state": "CLOSED"}]'), Result(1, "")))
monkeypatch.setattr(script.shutil, "which", lambda _name: "/usr/bin/gh")
monkeypatch.setattr(script.subprocess, "run", lambda *args, **kwargs: next(results))
assert script.load_states(None) == {1: "closed"}


def _docs(tmp_path, monkeypatch):
issues = tmp_path / "issues.json"
issues.write_text(json.dumps([{"number": n, "state": s.upper()} for n, s in STATES.items()]))
Expand Down
Loading