fix: raw_dir-Fixture kappt config.ROOT, private Fixture-Namen ersetzt (2.1.1)
Files changed: - CHANGES.md - VERSION - tools/chemenu/tests/conftest.py - tools/chemenu/tests/test_corpus_diff.py - tools/chemenu/tests/test_index_build.py - tools/chemenu/tests/test_kb_collections.py - tools/chemenu/tests/test_lint.py - tools/chemenu/tests/test_migrate_cmd.py - tools/chemenu/tests/test_new_page.py - tools/chemenu/tests/test_page_ops.py - tools/chemenu/tests/test_provenance.py - tools/chemenu/tests/test_search.py - tools/chemenu/tests/test_touch.py
This commit is contained in:
+37
@@ -20,6 +20,43 @@ their date-only headings.
|
||||
|
||||
---
|
||||
|
||||
## 2.1.1 - 2026-09-01 - raw_dir-Fixture kappt config.ROOT; letzte private Fixture-Namen ersetzt
|
||||
|
||||
**Author:** Torben Nehmer
|
||||
|
||||
Der erste CI-Lauf nach der Korpus-Bereinigung wurde rot, während dieselbe Suite lokal grün
|
||||
blieb: `test_legacy_source_pages_flags_url_and_directory` scheiterte mit
|
||||
`KeyError: 'Source - DirBacked'`.
|
||||
|
||||
**Die Ursache ist keine Testschwäche, sondern eine Umgebungsabhängigkeit.** Der Test legt eine
|
||||
Quellenseite mit `source: raw/documents` an und erwartet, dass `legacy_source_pages()` sie als
|
||||
„directory, not a file" meldet. Die Prüfung dort ist `(config.ROOT / legacy).is_dir()` — sie
|
||||
löst also gegen das *echte* Repo-Wurzelverzeichnis auf, während die `raw_dir`-Fixture ihren
|
||||
Baum unter `tmp_path` baut. Der Test war nie hermetisch; er bestand, weil dieser Checkout
|
||||
zufällig ein `raw/documents/` besaß.
|
||||
|
||||
Die Korpus-Bereinigung hat dieses Verzeichnis geleert. Git verfolgt keine leeren Verzeichnisse,
|
||||
also verschwand es im CI-Checkout und blieb im lokalen Arbeitsbaum stehen — grün hier, rot
|
||||
dort, ohne dass sich am Test etwas geändert hätte.
|
||||
|
||||
**Geschlossen in der Fixture, nicht im Test.** `raw_dir` setzt jetzt `config.ROOT` auf
|
||||
`tmp_path`. Das ist dieselbe Bauart und dasselbe Argument wie bei `hermetic_environment`
|
||||
(Gitea #8): Wenn eine Umgebungsabhängigkeit lokal unsichtbar ist, gehört sie an der Stelle
|
||||
geschlossen, die alle Tests erreicht, statt bei dem einen, der zufällig darüber stolpert.
|
||||
Nachgewiesen, indem `raw/documents/` lokal entfernt und die Suite erneut gefahren wurde — 38
|
||||
Provenance-Tests grün ohne das Verzeichnis, vorher rot.
|
||||
|
||||
**Fixture-Namen.** Die Suite benutzte weiterhin reale Systemnamen der Ursprungsinstanz als
|
||||
Fixture-Bezeichner (`atlantis`, `abydos`, `Nathan`, `hermes`, `ecodms`, `Opa Hasso`). In einem
|
||||
öffentlichen Repo beschreiben sie nichts, verraten aber die Namensgebung einer privaten
|
||||
Umgebung. Ersetzt durch `aurora`, `almanac`, `Borealis`, `gateway`, `docstore`. `gdeploy`
|
||||
bleibt: die Seite existiert im öffentlichen Korpus.
|
||||
|
||||
**Dateien:** `tools/chemenu/tests/conftest.py` (`raw_dir`), sämtliche Testdateien mit
|
||||
Fixture-Namen.
|
||||
|
||||
---
|
||||
|
||||
## 2.1.0 - 2026-09-01 - Dual-Lizenz AGPL-3.0/CC-BY-4.0, und ein Leak-Canary fuer dist export
|
||||
|
||||
**Author:** Torben Nehmer
|
||||
|
||||
@@ -3,6 +3,7 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from chemenu import config
|
||||
from chemenu.frontmatter_io import write_page
|
||||
|
||||
# Environment the tool reads for its own behaviour. Cleared for every test, so
|
||||
@@ -92,10 +93,26 @@ def isolated_trace_dir(
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def raw_dir(tmp_path: Path) -> Path:
|
||||
def raw_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
||||
"""A small fake raw/ tree sitting next to the kb_dir fixture (same
|
||||
tmp_path), for provenance-coverage tests. One file is deliberately left
|
||||
uncovered by any source page."""
|
||||
uncovered by any source page.
|
||||
|
||||
`config.ROOT` is repointed at `tmp_path` for the same reason
|
||||
`hermetic_environment` clears the environment: a `raw/...` path in a
|
||||
fixture page is resolved against the *repository* root by the code under
|
||||
test (`provenance.legacy_source_pages`, for one), so without this the
|
||||
fixture builds a raw tree that the code never looks at and answers from
|
||||
the developer's own `raw/` instead.
|
||||
|
||||
That was not theoretical. `test_legacy_source_pages_flags_url_and_directory`
|
||||
passed for months only because this checkout happened to have a
|
||||
`raw/documents/` directory; the run that emptied it turned the test red in
|
||||
CI while it stayed green locally, because git does not track empty
|
||||
directories and the local one survived. Same shape as Gitea #8, closed the
|
||||
same way - in the fixture, not in the one test that happened to trip.
|
||||
"""
|
||||
monkeypatch.setattr(config, "ROOT", tmp_path)
|
||||
raw = tmp_path / "raw"
|
||||
(raw / "notes").mkdir(parents=True)
|
||||
(raw / "notes" / "Aurora.md").write_text("# Aurora raw notes\n", encoding="utf-8")
|
||||
@@ -126,19 +143,19 @@ def kb_dir(tmp_path: Path) -> Path:
|
||||
{
|
||||
"type": "types/entity.md", "entity_type": "system",
|
||||
"tags": ["server"], "created": "2026-07-31", "modified": "2026-07-31",
|
||||
"related": ["Nathan"], "sources": [], "confidence": 0.9,
|
||||
"related": ["Borealis"], "sources": [], "confidence": 0.9,
|
||||
"summary": "Server hosting DocStore with ZFS storage",
|
||||
},
|
||||
"\n# aurora\n\n## Description\n\nHosts things.\n\n## Relationships\n\n- **Related to:** [[Nathan]]\n\n## See Also\n\n- [[Nathan]]\n",
|
||||
"\n# aurora\n\n## Description\n\nHosts things.\n\n## Relationships\n\n- **Related to:** [[Borealis]]\n\n## See Also\n\n- [[Borealis]]\n",
|
||||
)
|
||||
write_page(
|
||||
kb / "entities/systems/Nathan.md",
|
||||
kb / "entities/systems/Borealis.md",
|
||||
{
|
||||
"type": "types/entity.md", "entity_type": "system",
|
||||
"tags": ["workstation"], "created": "2026-08-02", "modified": "2026-08-02",
|
||||
"related": ["aurora"], "sources": [], "confidence": 0.9,
|
||||
},
|
||||
"\n# Nathan\n\n## Description\n\nA workstation.\n\n## Relationships\n\n- **Related to:** [[aurora]]\n",
|
||||
"\n# Borealis\n\n## Description\n\nA workstation.\n\n## Relationships\n\n- **Related to:** [[aurora]]\n",
|
||||
)
|
||||
write_page(
|
||||
kb / "entities/tools/gdeploy.md",
|
||||
@@ -169,7 +186,7 @@ def kb_dir(tmp_path: Path) -> Path:
|
||||
)
|
||||
|
||||
(kb / "index.md").write_text(
|
||||
"# Wiki Index\n\n[[aurora]] [[Nathan]] [[gdeploy]] [[Modbus]] [[Source - Aurora]]\n",
|
||||
"# Wiki Index\n\n[[aurora]] [[Borealis]] [[gdeploy]] [[Modbus]] [[Source - Aurora]]\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(kb / "log.md").write_text("# Wiki Log\n", encoding="utf-8")
|
||||
|
||||
@@ -15,8 +15,8 @@ from chemenu.page import Page
|
||||
BODY = """
|
||||
# Aurora
|
||||
|
||||
Runs [[DocStore]] and talks to [[Nathan]].[^src-notes]
|
||||
The second mention of [[Nathan]] is what a careless rewrite loses.
|
||||
Runs [[DocStore]] and talks to [[Borealis]].[^src-notes]
|
||||
The second mention of [[Borealis]] is what a careless rewrite loses.
|
||||
|
||||
## Fußnoten
|
||||
|
||||
@@ -31,7 +31,7 @@ def page(body: str, **frontmatter) -> Page:
|
||||
"created": "2026-07-31",
|
||||
"provenance": "sourced",
|
||||
"confidence_base": 0.9,
|
||||
"related": ["Nathan"],
|
||||
"related": ["Borealis"],
|
||||
"sources": ["Source - Aurora"],
|
||||
}
|
||||
base.update(frontmatter)
|
||||
@@ -57,7 +57,7 @@ def kinds(diff) -> list[str]:
|
||||
|
||||
|
||||
def test_a_dropped_occurrence_is_caught_even_though_the_link_set_is_unchanged():
|
||||
after = BODY.replace("The second mention of [[Nathan]] is", "Das zweite Vorkommen ist")
|
||||
after = BODY.replace("The second mention of [[Borealis]] is", "Das zweite Vorkommen ist")
|
||||
before_shapes, after_shapes = shapes(BODY, after)
|
||||
|
||||
# Precondition: the sets really are identical, so a set-based check passes.
|
||||
@@ -67,7 +67,7 @@ def test_a_dropped_occurrence_is_caught_even_though_the_link_set_is_unchanged():
|
||||
|
||||
diff = corpus_diff.compare(before_shapes, after_shapes)
|
||||
assert kinds(diff) == ["wikilinks"]
|
||||
assert "'Nathan' 2->1" in diff.findings[0].detail
|
||||
assert "'Borealis' 2->1" in diff.findings[0].detail
|
||||
assert not diff.ok
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ def test_pure_prose_change_is_not_reported():
|
||||
|
||||
|
||||
def test_a_dropped_citation_is_caught():
|
||||
after = BODY.replace("[[Nathan]].[^src-notes]", "[[Nathan]].")
|
||||
after = BODY.replace("[[Borealis]].[^src-notes]", "[[Borealis]].")
|
||||
diff = corpus_diff.compare(*shapes(BODY, after))
|
||||
assert "cite-refs" in kinds(diff)
|
||||
|
||||
|
||||
@@ -99,13 +99,13 @@ def test_map_deep_links_an_inlined_area_by_anchor(kb_dir):
|
||||
def test_collection_shard_holds_the_page_rows(plan, kb_dir):
|
||||
entities = _shard(plan, kb_dir, "entities")
|
||||
assert "[[aurora]]" in entities
|
||||
assert "[[Nathan]]" in entities
|
||||
assert "[[Borealis]]" in entities
|
||||
assert "[[gdeploy]]" in entities
|
||||
|
||||
|
||||
def test_pages_are_grouped_by_area_and_sorted_case_insensitively(plan, kb_dir):
|
||||
systems = _section(_shard(plan, kb_dir, "entities"), f"## {_area_title('system')}")
|
||||
assert systems.index("[[aurora]]") < systems.index("[[Nathan]]")
|
||||
assert systems.index("[[aurora]]") < systems.index("[[Borealis]]")
|
||||
assert "[[gdeploy]]" not in systems
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ def test_directory_without_a_contract_is_not_a_collection(repo):
|
||||
|
||||
|
||||
def test_area_resolves_to_its_enclosing_collection(repo):
|
||||
page = repo / "kb" / "entities" / "systems" / "hermes.md"
|
||||
page = repo / "kb" / "entities" / "systems" / "gateway.md"
|
||||
assert kb_collections.kb_collection_of(page) == repo / "kb" / "entities"
|
||||
|
||||
|
||||
|
||||
@@ -128,9 +128,9 @@ def test_lint_detects_orphan_page(kb_dir):
|
||||
)
|
||||
report = run_lint(kb_dir)
|
||||
assert "isolated" in report["orphan_pages"]
|
||||
# aurora and Nathan link to each other, so they must not be reported as orphans.
|
||||
# aurora and Borealis link to each other, so they must not be reported as orphans.
|
||||
assert "aurora" not in report["orphan_pages"]
|
||||
assert "Nathan" not in report["orphan_pages"]
|
||||
assert "Borealis" not in report["orphan_pages"]
|
||||
|
||||
|
||||
def test_lint_detects_missing_frontmatter_fields(kb_dir):
|
||||
|
||||
@@ -176,7 +176,7 @@ provenance: sourced
|
||||
|
||||
# Aurora
|
||||
|
||||
Links to [[Nathan]] and again to [[Nathan]].
|
||||
Links to [[Borealis]] and again to [[Borealis]].
|
||||
"""
|
||||
|
||||
|
||||
@@ -214,19 +214,19 @@ def test_verify_is_clean_when_only_prose_moved(git_instance, capsys):
|
||||
|
||||
def test_verify_catches_a_dropped_link_against_history(git_instance, capsys):
|
||||
page = git_instance / "kb" / "entities" / "Aurora.md"
|
||||
page.write_text(PAGE.replace(" and again to [[Nathan]]", ""), encoding="utf-8")
|
||||
page.write_text(PAGE.replace(" and again to [[Borealis]]", ""), encoding="utf-8")
|
||||
with pytest.raises(typer.Exit):
|
||||
migrate_cmd.verify_command(
|
||||
from_rev="HEAD", path=None, expect_body_change=False, json_out=True, fail_on_error=True
|
||||
)
|
||||
result = json.loads(capsys.readouterr().out)
|
||||
assert result["findings"][0]["kind"] == "wikilinks"
|
||||
assert "'Nathan' 2->1" in result["findings"][0]["detail"]
|
||||
assert "'Borealis' 2->1" in result["findings"][0]["detail"]
|
||||
|
||||
|
||||
def test_verify_scopes_to_a_subtree(git_instance, capsys):
|
||||
page = git_instance / "kb" / "entities" / "Aurora.md"
|
||||
page.write_text(PAGE.replace(" and again to [[Nathan]]", ""), encoding="utf-8")
|
||||
page.write_text(PAGE.replace(" and again to [[Borealis]]", ""), encoding="utf-8")
|
||||
migrate_cmd.verify_command(
|
||||
from_rev="HEAD",
|
||||
path=["kb/concepts"],
|
||||
|
||||
@@ -60,7 +60,7 @@ def test_new_entity_creates_page_with_expected_frontmatter(monkeypatch, kb_dir):
|
||||
result = _invoke_new(monkeypatch, kb_dir, [
|
||||
"new", "entity", "--name", "gateway.example.net",
|
||||
"--set", "entity_type=system", "--set", "tags=gateway,firewall",
|
||||
"--set", "related=Nathan", "--set", "confidence=0.9",
|
||||
"--set", "related=Borealis", "--set", "confidence=0.9",
|
||||
"--set", "provenance=general",
|
||||
])
|
||||
assert result.exit_code == 0, result.output
|
||||
@@ -70,7 +70,7 @@ def test_new_entity_creates_page_with_expected_frontmatter(monkeypatch, kb_dir):
|
||||
assert fm["type"] == "types/entity.md"
|
||||
assert fm["entity_type"] == "system"
|
||||
assert fm["tags"] == ["gateway", "firewall"]
|
||||
assert fm["related"] == ["Nathan"]
|
||||
assert fm["related"] == ["Borealis"]
|
||||
assert fm["confidence"] == 0.9
|
||||
assert "# gateway.example.net" in body
|
||||
|
||||
@@ -172,18 +172,18 @@ def test_new_source_prefixes_title_and_prefills_related_entities(monkeypatch, kb
|
||||
result = _invoke_new(monkeypatch, kb_dir, [
|
||||
"new", "source", "--name", "gateway.example.net",
|
||||
"--set", "raw_files=raw/notes/gateway.example.net.md",
|
||||
"--set", "entities=aurora,Nathan",
|
||||
"--set", "entities=aurora,Borealis",
|
||||
])
|
||||
assert result.exit_code == 0, result.output
|
||||
path = kb_dir / "sources/Source - gateway.example.net.md"
|
||||
assert path.exists()
|
||||
fm, body = read_page(path)
|
||||
assert fm["type"] == "types/source.md"
|
||||
assert fm["entities"] == ["aurora", "Nathan"]
|
||||
assert fm["entities"] == ["aurora", "Borealis"]
|
||||
assert fm["author"] == "Torben" # WIKI_AUTHOR override
|
||||
assert fm["source_type"] == "notes" # schema default
|
||||
assert "[[aurora]]" in body
|
||||
assert "[[Nathan]]" in body
|
||||
assert "[[Borealis]]" in body
|
||||
|
||||
|
||||
def test_new_source_author_falls_back_to_git_config(monkeypatch, kb_dir):
|
||||
@@ -264,13 +264,13 @@ def test_new_concept_rejects_invalid_provenance(monkeypatch, kb_dir):
|
||||
|
||||
def test_new_comparison_renders_table_columns(monkeypatch, kb_dir):
|
||||
result = _invoke_new(monkeypatch, kb_dir, [
|
||||
"new", "comparison", "--name", "A vs B", "--set", "entities=aurora,Nathan",
|
||||
"new", "comparison", "--name", "A vs B", "--set", "entities=aurora,Borealis",
|
||||
])
|
||||
assert result.exit_code == 0, result.output
|
||||
fm, body = read_page(kb_dir / "comparisons/A vs B.md")
|
||||
assert fm["entities"] == ["aurora", "Nathan"]
|
||||
assert fm["entities"] == ["aurora", "Borealis"]
|
||||
# Table columns are rendered by the generic table_* template filters.
|
||||
assert "[[aurora]] | [[Nathan]]" in body
|
||||
assert "[[aurora]] | [[Borealis]]" in body
|
||||
|
||||
|
||||
def test_new_comparison_rejects_single_entity(monkeypatch, kb_dir):
|
||||
|
||||
@@ -78,49 +78,49 @@ def test_retarget_cite_ids_is_noop_without_a_footnotes_block():
|
||||
|
||||
|
||||
def test_rename_refreshes_stale_slug_derived_cite_id(patched_wiki):
|
||||
old_id = cite_id("Nathan")
|
||||
old_id = cite_id("Borealis")
|
||||
write_page(
|
||||
patched_wiki / "concepts/uses-nathan.md",
|
||||
patched_wiki / "concepts/uses-borealis.md",
|
||||
{
|
||||
"type": "types/concept.md", "concept_type": "protocol",
|
||||
"tags": [], "created": "2026-07-25", "modified": "2026-07-25",
|
||||
"related": [], "sources": [], "confidence": 0.7,
|
||||
},
|
||||
f"\n# uses-nathan\n\nRuns on it [^{old_id}].\n\n## Footnotes\n\n[^{old_id}]: [[Nathan]]\n",
|
||||
f"\n# uses-borealis\n\nRuns on it [^{old_id}].\n\n## Footnotes\n\n[^{old_id}]: [[Borealis]]\n",
|
||||
)
|
||||
|
||||
page_ops.rename_command(old="Nathan", new="nathan-ws", dry_run=False)
|
||||
page_ops.rename_command(old="Borealis", new="borealis-ws", dry_run=False)
|
||||
|
||||
_frontmatter, body = read_page(patched_wiki / "concepts/uses-nathan.md")
|
||||
new_id = cite_id("nathan-ws")
|
||||
assert f"[^{new_id}]: [[nathan-ws]]" in body
|
||||
_frontmatter, body = read_page(patched_wiki / "concepts/uses-borealis.md")
|
||||
new_id = cite_id("borealis-ws")
|
||||
assert f"[^{new_id}]: [[borealis-ws]]" in body
|
||||
assert f"[^{old_id}]" not in body
|
||||
|
||||
|
||||
def test_rename_updates_body_links_and_frontmatter(patched_wiki):
|
||||
page_ops.rename_command(old="Nathan", new="nathan-ws", dry_run=False)
|
||||
page_ops.rename_command(old="Borealis", new="borealis-ws", dry_run=False)
|
||||
|
||||
assert (patched_wiki / "entities/systems/nathan-ws.md").exists()
|
||||
assert not (patched_wiki / "entities/systems/Nathan.md").exists()
|
||||
assert (patched_wiki / "entities/systems/borealis-ws.md").exists()
|
||||
assert not (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
|
||||
frontmatter, body = read_page(patched_wiki / "entities/systems/aurora.md")
|
||||
assert frontmatter["related"] == ["nathan-ws"]
|
||||
assert "[[nathan-ws]]" in body
|
||||
assert "[[Nathan]]" not in body
|
||||
assert frontmatter["related"] == ["borealis-ws"]
|
||||
assert "[[borealis-ws]]" in body
|
||||
assert "[[Borealis]]" not in body
|
||||
|
||||
|
||||
def test_rename_rewrites_the_pages_own_h1(patched_wiki):
|
||||
page_ops.rename_command(old="Nathan", new="nathan-ws", dry_run=False)
|
||||
_frontmatter, body = read_page(patched_wiki / "entities/systems/nathan-ws.md")
|
||||
assert "# nathan-ws" in body
|
||||
assert "# Nathan\n" not in body
|
||||
page_ops.rename_command(old="Borealis", new="borealis-ws", dry_run=False)
|
||||
_frontmatter, body = read_page(patched_wiki / "entities/systems/borealis-ws.md")
|
||||
assert "# borealis-ws" in body
|
||||
assert "# Borealis\n" not in body
|
||||
|
||||
|
||||
def test_rename_dry_run_writes_nothing(patched_wiki):
|
||||
page_ops.rename_command(old="Nathan", new="nathan-ws", dry_run=True)
|
||||
assert (patched_wiki / "entities/systems/Nathan.md").exists()
|
||||
page_ops.rename_command(old="Borealis", new="borealis-ws", dry_run=True)
|
||||
assert (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
frontmatter, _body = read_page(patched_wiki / "entities/systems/aurora.md")
|
||||
assert frontmatter["related"] == ["Nathan"]
|
||||
assert frontmatter["related"] == ["Borealis"]
|
||||
|
||||
|
||||
def test_rename_rejects_missing_page(patched_wiki):
|
||||
@@ -136,15 +136,15 @@ def test_rename_repoints_references_to_an_existing_page(patched_wiki):
|
||||
patched_wiki / "entities/tools/gdeploy.md",
|
||||
{"type": "types/entity.md", "entity_type": "tool", "tags": [],
|
||||
"created": "2026-07-25", "modified": "2026-07-25",
|
||||
"related": ["nathan"], "sources": [], "confidence": 0.8},
|
||||
"\n# gdeploy\n\n## See Also\n\n- [[nathan]]\n",
|
||||
"related": ["borealis"], "sources": [], "confidence": 0.8},
|
||||
"\n# gdeploy\n\n## See Also\n\n- [[borealis]]\n",
|
||||
)
|
||||
page_ops.rename_command(old="nathan", new="Nathan", dry_run=False)
|
||||
page_ops.rename_command(old="borealis", new="Borealis", dry_run=False)
|
||||
|
||||
frontmatter, body = read_page(patched_wiki / "entities/tools/gdeploy.md")
|
||||
assert frontmatter["related"] == ["Nathan"]
|
||||
assert "[[Nathan]]" in body
|
||||
assert (patched_wiki / "entities/systems/Nathan.md").exists()
|
||||
assert frontmatter["related"] == ["Borealis"]
|
||||
assert "[[Borealis]]" in body
|
||||
assert (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
|
||||
|
||||
def test_rename_reference_only_mode_requires_the_target_to_exist(patched_wiki):
|
||||
@@ -161,12 +161,12 @@ def test_rename_reference_only_mode_requires_the_target_to_exist(patched_wiki):
|
||||
|
||||
def test_rename_rejects_existing_target(patched_wiki):
|
||||
with pytest.raises(typer.Exit):
|
||||
page_ops.rename_command(old="Nathan", new="aurora", dry_run=False)
|
||||
page_ops.rename_command(old="Borealis", new="aurora", dry_run=False)
|
||||
|
||||
|
||||
def test_rename_rejects_identical_titles(patched_wiki):
|
||||
with pytest.raises(typer.Exit):
|
||||
page_ops.rename_command(old="Nathan", new="Nathan", dry_run=False)
|
||||
page_ops.rename_command(old="Borealis", new="Borealis", dry_run=False)
|
||||
|
||||
|
||||
def test_rename_fixes_a_dangling_source_reference(patched_wiki):
|
||||
@@ -201,24 +201,24 @@ def test_rename_stops_before_renaming_the_file_if_a_reference_write_fails(patche
|
||||
monkeypatch.setattr(page_ops_module, "write_page", flaky_write_page)
|
||||
|
||||
with pytest.raises(typer.Exit):
|
||||
page_ops.rename_command(old="Nathan", new="nathan-ws", dry_run=False)
|
||||
page_ops.rename_command(old="Borealis", new="borealis-ws", dry_run=False)
|
||||
|
||||
assert (patched_wiki / "entities/systems/Nathan.md").exists()
|
||||
assert not (patched_wiki / "entities/systems/nathan-ws.md").exists()
|
||||
assert (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
assert not (patched_wiki / "entities/systems/borealis-ws.md").exists()
|
||||
|
||||
|
||||
def test_rm_refuses_referenced_page_without_yes(patched_wiki):
|
||||
with pytest.raises(typer.Exit):
|
||||
page_ops.rm_command(page_title="Nathan", yes=False, dry_run=False)
|
||||
assert (patched_wiki / "entities/systems/Nathan.md").exists()
|
||||
page_ops.rm_command(page_title="Borealis", yes=False, dry_run=False)
|
||||
assert (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
|
||||
def test_rm_deletes_and_delinks_with_yes(patched_wiki):
|
||||
page_ops.rm_command(page_title="Nathan", yes=True, dry_run=False)
|
||||
page_ops.rm_command(page_title="Borealis", yes=True, dry_run=False)
|
||||
|
||||
assert not (patched_wiki / "entities/systems/Nathan.md").exists()
|
||||
assert not (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
frontmatter, body = read_page(patched_wiki / "entities/systems/aurora.md")
|
||||
assert frontmatter["related"] == []
|
||||
assert "[[Nathan]]" not in body
|
||||
assert "[[Borealis]]" not in body
|
||||
|
||||
|
||||
def test_rm_stops_before_deleting_the_file_if_a_delink_write_fails(patched_wiki, monkeypatch):
|
||||
@@ -234,9 +234,9 @@ def test_rm_stops_before_deleting_the_file_if_a_delink_write_fails(patched_wiki,
|
||||
monkeypatch.setattr(page_ops_module, "write_page", flaky_write_page)
|
||||
|
||||
with pytest.raises(typer.Exit):
|
||||
page_ops.rm_command(page_title="Nathan", yes=True, dry_run=False)
|
||||
page_ops.rm_command(page_title="Borealis", yes=True, dry_run=False)
|
||||
|
||||
assert (patched_wiki / "entities/systems/Nathan.md").exists()
|
||||
assert (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
|
||||
|
||||
def test_rm_of_unreferenced_page_needs_no_confirmation(patched_wiki):
|
||||
@@ -259,20 +259,20 @@ def test_rm_leaves_prose_references_and_reports_them(patched_wiki, capsys):
|
||||
{"type": "types/entity.md", "entity_type": "tool", "tags": [],
|
||||
"created": "2026-07-25", "modified": "2026-07-25", "related": [],
|
||||
"sources": [], "confidence": 0.8},
|
||||
"\n# gdeploy\n\n## Description\n\nRuns on [[Nathan]] nightly.\n",
|
||||
"\n# gdeploy\n\n## Description\n\nRuns on [[Borealis]] nightly.\n",
|
||||
)
|
||||
page_ops.rm_command(page_title="Nathan", yes=True, dry_run=False)
|
||||
page_ops.rm_command(page_title="Borealis", yes=True, dry_run=False)
|
||||
|
||||
_frontmatter, body = read_page(patched_wiki / "entities/tools/gdeploy.md")
|
||||
assert "Runs on [[Nathan]] nightly." in body
|
||||
assert "remaining [[Nathan]] reference(s)" in capsys.readouterr().out
|
||||
assert "Runs on [[Borealis]] nightly." in body
|
||||
assert "remaining [[Borealis]] reference(s)" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_rm_dry_run_writes_nothing(patched_wiki):
|
||||
page_ops.rm_command(page_title="Nathan", yes=True, dry_run=True)
|
||||
assert (patched_wiki / "entities/systems/Nathan.md").exists()
|
||||
page_ops.rm_command(page_title="Borealis", yes=True, dry_run=True)
|
||||
assert (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
frontmatter, _body = read_page(patched_wiki / "entities/systems/aurora.md")
|
||||
assert frontmatter["related"] == ["Nathan"]
|
||||
assert frontmatter["related"] == ["Borealis"]
|
||||
|
||||
|
||||
def test_inbound_pages_sees_frontmatter_only_references(patched_wiki):
|
||||
|
||||
@@ -161,7 +161,7 @@ def test_the_block_is_re_emitted_last_so_the_layout_self_heals():
|
||||
body = (
|
||||
"# Page\n\nProse [^s-aurora].\n\n"
|
||||
+ render_cite_block(definitions)
|
||||
+ "\n## Siehe auch\n\n- [[Nathan]]\n"
|
||||
+ "\n## Siehe auch\n\n- [[Borealis]]\n"
|
||||
)
|
||||
rebuilt = render_page_body(*split_cite_block(body))
|
||||
assert rebuilt.index("## Siehe auch") < rebuilt.index("[^s-aurora]:")
|
||||
@@ -173,7 +173,7 @@ def test_repeated_round_trips_are_stable():
|
||||
body = (
|
||||
"# Page\n\nProse [^s-aurora].\n\n"
|
||||
+ render_cite_block(definitions)
|
||||
+ "\n## Siehe auch\n\n- [[Nathan]]\n"
|
||||
+ "\n## Siehe auch\n\n- [[Borealis]]\n"
|
||||
)
|
||||
once = render_page_body(*split_cite_block(body))
|
||||
twice = render_page_body(*split_cite_block(once))
|
||||
@@ -200,7 +200,7 @@ def test_a_citation_used_in_a_rescued_section_still_resolves():
|
||||
"# Page\n\nProse.\n\n"
|
||||
"## Fußnoten\n\n"
|
||||
"[^s-aurora]: [[Source - Aurora]]\n\n"
|
||||
"## Beziehungen\n\n- **belegt durch:** [[Nathan]] [^s-aurora]\n"
|
||||
"## Beziehungen\n\n- **belegt durch:** [[Borealis]] [^s-aurora]\n"
|
||||
)
|
||||
assert extract_inline_cites(body) == {("Source - Aurora", None)}
|
||||
|
||||
@@ -368,7 +368,7 @@ def test_sources_trace_by_raw_and_by_page(kb_dir, raw_dir, monkeypatch):
|
||||
kb_dir / "entities/systems/aurora.md",
|
||||
{
|
||||
"type": "entity", "entity_type": "system", "tags": ["server"],
|
||||
"created": "2026-07-31", "modified": "2026-07-31", "related": ["Nathan"],
|
||||
"created": "2026-07-31", "modified": "2026-07-31", "related": ["Borealis"],
|
||||
"sources": ["Source - Aurora"], "confidence": 0.9,
|
||||
},
|
||||
"\n# aurora\n\n## Description\n\nHosts things.\n",
|
||||
|
||||
@@ -74,7 +74,7 @@ def test_parse_predicate_rejects_malformed(raw):
|
||||
|
||||
|
||||
def test_exact_match_on_frontmatter_field(search):
|
||||
assert _titles(search(_q("entity_type=system"))) == ["aurora", "Nathan"]
|
||||
assert _titles(search(_q("entity_type=system"))) == ["aurora", "Borealis"]
|
||||
|
||||
|
||||
def test_membership_on_list_field(search):
|
||||
@@ -86,13 +86,13 @@ def test_substring_match_is_case_insensitive(search):
|
||||
|
||||
|
||||
def test_numeric_comparison(search):
|
||||
assert _titles(search(_q("confidence>=0.9"))) == ["aurora", "Nathan"]
|
||||
assert _titles(search(_q("confidence>=0.9"))) == ["aurora", "Borealis"]
|
||||
|
||||
|
||||
def test_date_comparison_handles_yaml_date_objects(search):
|
||||
"""PyYAML parses `modified: 2026-08-02` into a date, not a string - the
|
||||
comparison has to normalise it or it never matches."""
|
||||
assert _titles(search(_q("modified>=2026-08-01"))) == ["Nathan"]
|
||||
assert _titles(search(_q("modified>=2026-08-01"))) == ["Borealis"]
|
||||
|
||||
|
||||
def test_exists_and_absent_are_complementary(search, pages):
|
||||
|
||||
@@ -172,7 +172,7 @@ def test_page_reference_fields_are_refused_and_name_xref(touch_wiki, capsys):
|
||||
frontmatter write would leave the other half stale."""
|
||||
for field in ("related", "sources", "entities", "concepts"):
|
||||
with pytest.raises(typer.Exit):
|
||||
_touch(page_title="aurora", set_fields=[f"{field}=Nathan"])
|
||||
_touch(page_title="aurora", set_fields=[f"{field}=Borealis"])
|
||||
assert "xref" in capsys.readouterr().out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user