177c7e9ce8
Files changed: - .gitea/workflows/ci.yml - AGENTS.md - CHANGES.md - VERSION - instructions/CONTRACT.md - instructions/link-taxonomy.md - instructions/migrations/4.0.0-link-taxonomy.md - instructions/setup-instance.md - kb/CONTRACT.md - kb/CONVENTIONS.md - kb/CONVENTIONS.md.template - kb/comparisons/COLLECTION.md - kb/concepts/COLLECTION.md - kb/entities/COLLECTION.md - kb/sources/COLLECTION.md - tools/CONTRACT.md - tools/README.md - tools/chemenu/blocks.py - tools/chemenu/cli.py - tools/chemenu/commands/cite_cmd.py - tools/chemenu/commands/dist_cmd.py - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/doctor.py - tools/chemenu/commands/links_cmd.py - tools/chemenu/commands/migrate_cmd.py - tools/chemenu/commands/new_page.py - tools/chemenu/commands/page_ops.py - tools/chemenu/commands/run_budget.py - tools/chemenu/commands/xref.py - tools/chemenu/conventions.py - tools/chemenu/corpus_diff.py - tools/chemenu/frontmatter_io.py - tools/chemenu/kb_collections.py - tools/chemenu/kb_state.py - tools/chemenu/links.py - tools/chemenu/lint_core.py - tools/chemenu/provenance.py - tools/chemenu/sections.py - tools/chemenu/tests/conftest.py - tools/chemenu/tests/test_blocks.py - tools/chemenu/tests/test_cite_cmd.py - tools/chemenu/tests/test_conventions.py - tools/chemenu/tests/test_dist_cmd.py - tools/chemenu/tests/test_doctor.py - tools/chemenu/tests/test_migrate_cmd.py - tools/chemenu/tests/test_new_page.py - tools/chemenu/tests/test_pipeline_l0.py - tools/chemenu/tests/test_types_cmd.py - tools/chemenu/tests/test_xref.py - types/concept.schema.yaml - types/entity.md - types/entity.schema.yaml - types/instruction.schema.yaml - types/type-spec.md - work/link-taxonomy-migration/README.md - work/link-taxonomy-migration/plan.md
91 lines
3.9 KiB
Python
91 lines
3.9 KiB
Python
"""Tests for generated regions - the delimiters that replaced heading matching.
|
|
|
|
The whole point is that a region's *identity* stops depending on its heading
|
|
text. Everything here is about the two questions the old approach answered by
|
|
guessing: where does the region start, and where does it stop.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from chemenu import blocks
|
|
|
|
PROSE = "# Page\n\n## Beschreibung\n\nProse.\n"
|
|
|
|
|
|
def _links(heading="Beziehungen", lines=("- **uses:** [[X]]",)):
|
|
return blocks.render(blocks.LINKS, heading, list(lines))
|
|
|
|
|
|
def test_a_region_round_trips():
|
|
body = blocks.replace(PROSE, blocks.LINKS, _links())
|
|
assert blocks.find(body, blocks.LINKS) == "## Beziehungen\n\n- **uses:** [[X]]"
|
|
assert blocks.strip(body, blocks.LINKS) == PROSE
|
|
|
|
|
|
def test_replacing_does_not_append_a_second_region():
|
|
body = blocks.replace(PROSE, blocks.LINKS, _links())
|
|
again = blocks.replace(body, blocks.LINKS, _links(lines=["- **uses:** [[Y]]"]))
|
|
assert again.count(blocks.open_marker(blocks.LINKS)) == 1
|
|
assert "[[X]]" not in again and "[[Y]]" in again
|
|
|
|
|
|
def test_the_heading_inside_a_region_is_not_how_it_is_found():
|
|
"""A page whose region carries a heading the instance never declared - an
|
|
unconverted page, a hand-edit, another language - is still located exactly.
|
|
Under heading matching this was the case that silently created a second
|
|
section."""
|
|
body = blocks.replace(PROSE, blocks.LINKS, _links(heading="Something Else Entirely"))
|
|
assert "[[X]]" in blocks.find(body, blocks.LINKS)
|
|
assert blocks.strip(body, blocks.LINKS) == PROSE
|
|
|
|
|
|
def test_content_after_a_region_survives_a_rewrite():
|
|
"""The eight-page bug, as a test. The old block ran to the next heading -
|
|
and before that to the end of the file - so anything sitting after it was
|
|
deleted on the next write."""
|
|
body = blocks.replace(PROSE, blocks.LINKS, _links()) + "\n## Afterwards\n\nKeep me.\n"
|
|
rewritten = blocks.replace(body, blocks.LINKS, _links(lines=["- **uses:** [[Z]]"]))
|
|
assert "Keep me." in rewritten
|
|
assert rewritten.count("## Afterwards") == 1
|
|
|
|
|
|
def test_two_regions_coexist_without_reading_each_other():
|
|
body = blocks.replace(PROSE, blocks.LINKS, _links())
|
|
body = blocks.replace(
|
|
body, blocks.FOOTNOTES,
|
|
blocks.render(blocks.FOOTNOTES, "Fußnoten", ["[^s-x]: [[Source - X]]"]),
|
|
)
|
|
assert "[[X]]" in blocks.find(body, blocks.LINKS)
|
|
assert "[^s-x]" in blocks.find(body, blocks.FOOTNOTES)
|
|
assert blocks.marker_pairs(body) == {"links": 1, "footnotes": 1}
|
|
|
|
|
|
def test_an_empty_region_is_no_region_at_all():
|
|
"""A page that cites nothing must not carry an empty Footnotes heading."""
|
|
assert blocks.render(blocks.LINKS, "Beziehungen", []) == ""
|
|
body = blocks.replace(PROSE, blocks.LINKS, _links())
|
|
assert blocks.replace(body, blocks.LINKS, "") == PROSE
|
|
|
|
|
|
def test_an_absent_region_reads_as_none_not_as_empty():
|
|
"""None and "" have to stay distinguishable: one means the page has no
|
|
region, the other that it has one holding nothing."""
|
|
assert blocks.find(PROSE, blocks.LINKS) is None
|
|
|
|
|
|
def test_a_dropped_marker_is_detectable():
|
|
"""An agent rewriting prose at the boundary can lose one. Silent otherwise:
|
|
the region becomes ordinary prose and the next write appends a second one
|
|
beside it."""
|
|
body = blocks.replace(PROSE, blocks.LINKS, _links())
|
|
assert blocks.unbalanced_markers(body) == []
|
|
assert blocks.unbalanced_markers(body.replace(blocks.close_marker(blocks.LINKS), "")) == ["links"]
|
|
assert blocks.unbalanced_markers(body.replace(blocks.open_marker(blocks.LINKS), "")) == ["links"]
|
|
|
|
|
|
def test_marker_pairs_counts_rather_than_sets():
|
|
"""A page that went from one region to two has the same set of names and a
|
|
different count - which is why `migrate verify` compares counts."""
|
|
body = blocks.replace(PROSE, blocks.LINKS, _links())
|
|
doubled = body + "\n" + _links() + "\n"
|
|
assert blocks.marker_pairs(doubled) == {"links": 2}
|