"""Regression tests for defects found in the independent QA run (.qa/QA_REPORT.md, IDs QA2-…).""" import re from conftest import Book, Doc def _pipe_rows(text: str) -> list[str]: return [ln for ln in text.splitlines() if ln.startswith("| ")] def test_pipe_in_inline_code_does_not_split_gfm_table(convert): """QA2-01: GFM splits table cells at every unescaped '|', also inside code spans and link targets.""" body = ("
| Operator | Bedeutung |
|---|---|
|| | logisches ODER |
a | b | Link |
Text1.
' '' 'Zweite2.
' '' "Danach kommt normaler Text.
") def _fences_closed(text: str) -> bool: """Every opening ``` fence has a closing line that consists only of backticks.""" open_fence = None for line in text.splitlines(): s = line.strip() if open_fence is None: m = re.match(r"(`{3,})", s) if m: open_fence = m.group(1) elif re.fullmatch(r"`{3,}", s) and len(s) >= len(open_fence): open_fence = None return open_fence is None def test_backlink_after_code_block_does_not_break_fence_commonmark(convert): """QA2-02: the ↩ backlink was appended to the closing ``` line, leaving the fence open.""" run = convert(Book(docs=[Doc("c.xhtml", NOTE_WITH_CODE)]), "--format", "commonmark") text = run.chapter("001") assert _fences_closed(text), text assert not re.search(r"\*\*\d+[:.]\*\* ```", text), "Fence hinter Notennummer ist kein Codeblock" assert "[↩](#fnref-001)" in text def test_backlink_after_code_block_does_not_break_fence_end_of_book(convert): run = convert(Book(docs=[Doc("c.xhtml", NOTE_WITH_CODE)]), "--footnotes", "end-of-book") notes = run.chapter("999") assert _fences_closed(notes), notes assert not re.search(r"\*\*\d+[:.]\*\* ```", notes), "Fence hinter Notennummer ist kein Codeblock" assert "[↩](001-k.md#fnref-001)" in notes NOTE_IN_HTML_TABLE = ('| Zelle1 | |
| a | b |
Text1.
' '') def test_note_referenced_from_html_table_and_text_has_target(convert): """QA2-03: GFM: the HTML-table reference pointed to #fn-001, but only a [^fn-001] definition existed.""" run = convert(Book(docs=[Doc("c.xhtml", NOTE_IN_HTML_TABLE)])) text = run.chapter("001") assert 'href="#fn-001"' in text and "Text[^fn-001]." in text assert re.search(r'id="fn-001"', text), text assert run.report["output_link_check"]["broken_count"] == 0 def _html_table(text: str) -> str: return text[text.index("| Kopf | |
|---|---|
| A | ' '' " |
Vorher
{code}Nachher
" text = convert(Book(docs=[Doc("c.xhtml", body)])).chapter("001") assert f"```python\n{code}\n```" in text, text assert "\n\n\n" not in text.replace(code, "") # outside code blank lines are still normalized def test_dash_and_equals_lines_stay_text(convert): """QA2-08: a paragraph '---' became a thematic break, '===' after a line break a setext heading.""" body = "---
Zeile
===
Text
-----
Text.
")]) def test_report_must_not_overwrite_generated_output_file(convert, tmp_path): """QA2-07: --report OUTPUT/manifest.json silently replaced the manifest with the report.""" out = tmp_path / "ziel" run = convert(_simple(), "--report", str(out / "manifest.json"), out=out) assert run.rc == 1 assert "--report" in run.stderr assert not out.exists() def test_report_write_error_is_input_error_not_internal(convert, tmp_path): """QA2-07: an unwritable --report target ended with exit 4 ("Interner Fehler").""" import os import stat ro = tmp_path / "ro" ro.mkdir() os.chmod(ro, stat.S_IRUSR | stat.S_IXUSR) try: run = convert(_simple(), "--report", str(ro / "bericht.json"), out=tmp_path / "ziel") finally: os.chmod(ro, stat.S_IRWXU) assert run.rc == 1, run.stderr assert "Interner Fehler" not in run.stderr assert "Bericht" in run.stderr