fix: a wikilink wrapped across a line break is its own lint finding; rename and rm see it (#115)
Files changed: - CHANGES.md - VERSION - instructions/wiki-lint/SKILL.md - kb/CONTRACT.md - tools/CONTRACT.md - tools/chemenu/commands/lint.py - tools/chemenu/commands/page_ops.py - tools/chemenu/kb_scan.py - tools/chemenu/lint_core.py - tools/chemenu/tests/test_kb_scan.py - tools/chemenu/tests/test_lint.py - tools/chemenu/tests/test_page_ops.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
This commit is contained in:
1 parent
b3022b8ffb
commit
c261b8f4ca
12 files changed
+204
-13
No files matched your search
+15
-2
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
|
||||
|
||||
---
|
||||
|
||||
## 8.0.0-beta.29 - 2026-10-03 - stack-close no longer records which model and effort ran each phase
|
||||
## 8.0.0-beta.30 - 2026-10-03 - lint: a wikilink wrapped across a line break is its own finding; rename and rm see it
|
||||
|
||||
**Author:** Torben Nehmer
|
||||
|
||||
@@ -140,9 +140,22 @@ concern - readable here, never shipped as something to parse.
|
||||
- kb/CONTRACT.md: an external article's raw_files point under raw/, a raw fetch capture names both files
|
||||
- raw fetch --html record and kb/CONTRACT.md path budget follow the folder accept
|
||||
- stack-close no longer records which model and effort ran each phase
|
||||
- lint: a wikilink wrapped across a line break is its own finding; rename and rm see it
|
||||
<!-- /wikitool:bumps -->
|
||||
|
||||
### stack-close no longer records which model and effort ran each phase
|
||||
### lint: a wikilink wrapped across a line break is its own finding; rename and rm see it
|
||||
|
||||
A `[[...]]` written across a line break - prose wrapped at a fixed column, with the break
|
||||
landing inside the link - was read with the break as part of its title. `lint` reported the
|
||||
target as a missing page, with the line break in the printed name; `rm`'s inbound check did not
|
||||
count it, so a page referenced only that way was deleted without the `--yes` confirmation; and
|
||||
`rename` left it pointing at the old title. A link target is now read through one function,
|
||||
`kb_scan.normalize_link_target()`, which folds a line break and its indentation to one space -
|
||||
used by the link graph, the index check, `rename` and `rm` alike. That such a link is wrapped
|
||||
is still a defect: `lint` reports it in a new hard section, *Wrapped Wikilinks*, and reports it
|
||||
as broken as well only if the folded title is missing. No instance's lint gets redder: every
|
||||
link the new section names was a hard broken-link finding before. `kb/CONTRACT.md` § Titles are
|
||||
identifiers states the rule (Gitea #115).
|
||||
|
||||
`stack-close`'s closing comment no longer carries a per-phase table of model, effort, session
|
||||
shape and compaction, nor the `size/` label beside it; the comment is the changelog line, then
|
||||
|
||||
@@ -41,7 +41,8 @@ mechanical half looks exactly like a complete one.
|
||||
|
||||
No flags: prints the sections that found something, writes the full report to
|
||||
`reports/Lint Report <YYYY-MM-DD>.md`, and names that path. This deterministically finds
|
||||
unreadable frontmatter, broken wikilinks, dangling frontmatter references, orphan pages,
|
||||
unreadable frontmatter, broken wikilinks, wikilinks wrapped across a line break, dangling
|
||||
frontmatter references, orphan pages,
|
||||
catalog drift, missing fields, duplicate titles, titles that are not valid, unique file
|
||||
names on Windows and macOS, filename/title mismatches, broken
|
||||
`raw_files:` references, raw files claimed by more than one source page, invalid type paths,
|
||||
|
||||
@@ -116,6 +116,12 @@ not a naming preference; it is the wiki's only way to address a page. `wikitool
|
||||
H1 that stops matching its title, `rename`/`rm` rewrite every reference to a stem, and a
|
||||
`[^cite-id]` resolves through one.
|
||||
|
||||
**A wikilink is one token and is never wrapped across lines.** When prose is broken at a fixed
|
||||
column, the break goes before or after `[[...]]`, never inside it: a renderer does not reliably
|
||||
read a link with a line break in it as a link. `wikitool lint` reports one as a *Wrapped
|
||||
Wikilinks* hard error, naming the title it folds to - the graph, `rename` and `rm` already read
|
||||
it as that title, so the fix is only to put it back on one line.
|
||||
|
||||
Which *form* those titles take - spaces or kebab-case, singular or plural, what prefixes a
|
||||
decision record - is the instance's, in
|
||||
[kb/CONVENTIONS.md § Naming](CONVENTIONS.md#naming).
|
||||
|
||||
+2
-1
@@ -1120,8 +1120,9 @@ Run structural lint checks against kb/.
|
||||
|
||||
**NOTES**
|
||||
|
||||
- Structural and provenance checks over `kb/`: broken wikilinks, dangling frontmatter references, orphan pages, index drift, schema gaps, duplicate titles, title mismatches, uncovered raw files, broken `raw_files:` refs, raw files claimed by more than one source page, unmarked provenance, citation/frontmatter drift, and unbalanced generated-region markers.
|
||||
- Structural and provenance checks over `kb/`: broken wikilinks, wikilinks wrapped across a line break, dangling frontmatter references, orphan pages, index drift, schema gaps, duplicate titles, title mismatches, uncovered raw files, broken `raw_files:` refs, raw files claimed by more than one source page, unmarked provenance, citation/frontmatter drift, and unbalanced generated-region markers.
|
||||
- Unportable Titles is a hard finding, and hard at every `kb_version`: a page whose title is not a valid file name on Windows and macOS (forbidden character, reserved name, trailing dot or space), or that collides with another page by case or Unicode normalization. `wikitool rename` is the fix.
|
||||
- Wrapped Wikilinks is a hard finding: a `[[...]]` with a line break inside it. The link graph reads it as the title it folds to (the break and its indentation become one space), so it is not also a broken link unless that title is missing; the fix is to put it back on one line.
|
||||
- Pages nested more than one directory below their collection are a hard finding - the generated catalog folds these into their area silently rather than merely reading it.
|
||||
- Edges whose label is missing or not authorised by the source collection's `outbound:` are both hard once `kb_version` has reached the release that introduced labelled edges, and advisory below it.
|
||||
- Advisory only: `see-also` edges whose reverse direction already carries a specific label - never migration-gated.
|
||||
|
||||
@@ -59,7 +59,8 @@ __all__ = [
|
||||
budget=cli_contract.Budget.COUNTED,
|
||||
),
|
||||
notes=(
|
||||
"Structural and provenance checks over `kb/`: broken wikilinks, dangling frontmatter "
|
||||
"Structural and provenance checks over `kb/`: broken wikilinks, wikilinks wrapped "
|
||||
"across a line break, dangling frontmatter "
|
||||
"references, orphan pages, index drift, schema gaps, duplicate titles, title "
|
||||
"mismatches, uncovered raw files, broken `raw_files:` refs, raw files claimed by more "
|
||||
"than one source page, unmarked provenance, citation/frontmatter drift, and unbalanced "
|
||||
@@ -68,6 +69,10 @@ __all__ = [
|
||||
"is not a valid file name on Windows and macOS (forbidden character, reserved name, "
|
||||
"trailing dot or space), or that collides with another page by case or Unicode "
|
||||
"normalization. `wikitool rename` is the fix.",
|
||||
"Wrapped Wikilinks is a hard finding: a `[[...]]` with a line break inside it. The link "
|
||||
"graph reads it as the title it folds to (the break and its indentation become one "
|
||||
"space), so it is not also a broken link unless that title is missing; the fix is to put "
|
||||
"it back on one line.",
|
||||
"Pages nested more than one directory below their collection are a hard finding - the "
|
||||
"generated catalog folds these into their area silently rather than merely reading it.",
|
||||
"Edges whose label is missing or not authorised by the source collection's `outbound:` "
|
||||
|
||||
@@ -40,7 +40,7 @@ from chemenu.commands._util import (
|
||||
from chemenu.frontmatter_io import write_page
|
||||
from chemenu.lint_core import find_misplaced
|
||||
from chemenu.page import Page
|
||||
from chemenu.kb_scan import load_kb_pages
|
||||
from chemenu.kb_scan import load_kb_pages, normalize_link_target
|
||||
from chemenu.provenance import (
|
||||
CITE_REF_RE,
|
||||
cite_id,
|
||||
@@ -55,6 +55,8 @@ from chemenu.type_resolver import resolver
|
||||
# is exactly what lets retarget_body() repoint a citation's link target on a
|
||||
# rename. Group 1 is the target title; group 2 keeps any alias/anchor suffix
|
||||
# untouched. The id itself is a separate concern - see retarget_cite_ids().
|
||||
# Group 1 may span a line break; it is compared through
|
||||
# kb_scan.normalize_link_target(), the same reading `lint` gives it.
|
||||
LINK_RE = re.compile(r"\[\[([^\[\]|#]+)((?:[|#][^\[\]]*)?)\]\]")
|
||||
|
||||
|
||||
@@ -76,7 +78,7 @@ def retarget_body(body: str, old: str, new: str) -> str:
|
||||
|
||||
def replace(match: re.Match) -> str:
|
||||
target, suffix = match.group(1), match.group(2)
|
||||
if target.strip() != old:
|
||||
if normalize_link_target(target) != old:
|
||||
return match.group(0)
|
||||
return f"[[{new}{suffix}]]"
|
||||
|
||||
@@ -202,7 +204,9 @@ def strip_link_bullets(body: str, title: str) -> str:
|
||||
|
||||
def body_references(body: str, title: str) -> int:
|
||||
"""How many wikilinks in `body` still point at `title`."""
|
||||
return sum(1 for match in LINK_RE.finditer(body) if match.group(1).strip() == title)
|
||||
return sum(
|
||||
1 for match in LINK_RE.finditer(body) if normalize_link_target(match.group(1)) == title
|
||||
)
|
||||
|
||||
|
||||
def inbound_pages(pages: dict[str, Page], title: str) -> list[str]:
|
||||
|
||||
@@ -12,6 +12,25 @@ from chemenu.page import Page
|
||||
|
||||
WIKILINK_RE = re.compile(r"\[\[([^\]|#]+)")
|
||||
|
||||
# A line break inside `[[...]]`, with the indentation around it. The target
|
||||
# class above admits a newline, so a link someone wrapped at a fixed column -
|
||||
# `[[Foo Bar\n Target]]` - captures the break as part of the title, matches no
|
||||
# page, and read as a missing one to `lint`, as no reference at all to `rm`'s
|
||||
# inbound check, and as nothing to repoint to `rename`.
|
||||
_LINE_BREAK_RUN = re.compile(r"[ \t]*(?:\r?\n[ \t]*)+")
|
||||
|
||||
|
||||
def normalize_link_target(raw: str) -> str:
|
||||
"""The title a captured wikilink target names: every whitespace run that
|
||||
contains a line break folded to one space, then stripped.
|
||||
|
||||
Every reader of a body wikilink goes through this, so `lint`, the link
|
||||
graph, `rename` and `rm` agree on what a wrapped link points at. That the
|
||||
link is wrapped at all is still a finding - `wrapped_wikilinks` - because
|
||||
folding it here would otherwise make it invisible.
|
||||
"""
|
||||
return _LINE_BREAK_RUN.sub(" ", raw).strip()
|
||||
|
||||
|
||||
# Root-level files under kb/ that are not pages: the generated catalog map, log
|
||||
# and provenance index, plus the two documents that constrain the tree rather
|
||||
@@ -105,7 +124,24 @@ def extract_wikilinks(body: str) -> set[str]:
|
||||
notation, and counting it made a page that documents the wiki look like it
|
||||
linked to something that need not exist.
|
||||
"""
|
||||
return {m.group(1).strip() for m in WIKILINK_RE.finditer(strip_code_spans(body))}
|
||||
return {
|
||||
normalize_link_target(m.group(1)) for m in WIKILINK_RE.finditer(strip_code_spans(body))
|
||||
}
|
||||
|
||||
|
||||
def wrapped_wikilinks(body: str) -> list[str]:
|
||||
"""The normalized targets of every wikilink in this body written across a
|
||||
line break, in order of appearance, code masked out as everywhere else.
|
||||
|
||||
A renderer does not reliably read such a link as one, and the title rule
|
||||
(`kb/CONTRACT.md` § Titles are identifiers) has no room for it - so it is
|
||||
reported on its own, whether or not the folded title names a page.
|
||||
"""
|
||||
return [
|
||||
normalize_link_target(m.group(1))
|
||||
for m in WIKILINK_RE.finditer(strip_code_spans(body))
|
||||
if "\n" in m.group(1)
|
||||
]
|
||||
|
||||
|
||||
def count_wikilinks(body: str) -> Counter[str]:
|
||||
@@ -117,7 +153,9 @@ def count_wikilinks(body: str) -> Counter[str]:
|
||||
the 248-page German translation were exactly that shape, and a set-based
|
||||
comparison reported all three as clean.
|
||||
"""
|
||||
return Counter(m.group(1).strip() for m in WIKILINK_RE.finditer(strip_code_spans(body)))
|
||||
return Counter(
|
||||
normalize_link_target(m.group(1)) for m in WIKILINK_RE.finditer(strip_code_spans(body))
|
||||
)
|
||||
|
||||
|
||||
def find_nested_pages(kb_dir: Path, pages: dict[str, Page]) -> list[tuple[str, Page, int]]:
|
||||
|
||||
@@ -42,6 +42,8 @@ from chemenu.kb_scan import (
|
||||
inbound_links,
|
||||
iter_kb_pages,
|
||||
load_kb_pages,
|
||||
normalize_link_target,
|
||||
wrapped_wikilinks,
|
||||
)
|
||||
from chemenu.titles import PATH_BUDGET, collision_key, path_length, title_problems
|
||||
from chemenu.type_resolver import resolver
|
||||
@@ -323,6 +325,14 @@ def run_lint(kb_dir: Path) -> dict:
|
||||
for target in sorted(targets)
|
||||
if target not in pages
|
||||
]
|
||||
# The graph above already reads a wrapped link as the title it folds to,
|
||||
# so a wrapped link to an existing page is no longer a broken one. It is
|
||||
# still a defect of its own - see kb_scan.wrapped_wikilinks().
|
||||
wrapped_links = [
|
||||
{"page": title, "target": target}
|
||||
for title, page in sorted(pages.items())
|
||||
for target in sorted(set(wrapped_wikilinks(page.body)))
|
||||
]
|
||||
|
||||
inbound = inbound_links({t: v for t, v in graph.items() if t != "index"})
|
||||
orphan_pages = sorted(
|
||||
@@ -352,7 +362,7 @@ def run_lint(kb_dir: Path) -> dict:
|
||||
for path in [kb_dir / "index.md", *sorted(kb_dir.rglob(GENERATED_INDEX))]
|
||||
if path.exists()
|
||||
)
|
||||
index_links = {m.group(1).strip() for m in WIKILINK_RE.finditer(index_text)}
|
||||
index_links = {normalize_link_target(m.group(1)) for m in WIKILINK_RE.finditer(index_text)}
|
||||
missing_from_index = sorted(set(pages) - index_links - {"index", "log"})
|
||||
dangling_index_entries = sorted(index_links - set(pages))
|
||||
|
||||
@@ -539,6 +549,7 @@ def run_lint(kb_dir: Path) -> dict:
|
||||
"page_count": len(pages),
|
||||
"frontmatter_errors": frontmatter_errors,
|
||||
"broken_links": broken_links,
|
||||
"wrapped_wikilinks": wrapped_links,
|
||||
"orphan_pages": orphan_pages,
|
||||
"most_linked": most_linked,
|
||||
"inbound_counts": inbound_counts,
|
||||
@@ -600,6 +611,10 @@ def render_markdown(report: dict) -> str:
|
||||
lines, "Broken Wikilinks", report["broken_links"],
|
||||
lambda i: f"[[{i['page']}]] links to missing [[{i['target']}]]",
|
||||
)
|
||||
_section(
|
||||
lines, "Wrapped Wikilinks", report.get("wrapped_wikilinks", []),
|
||||
lambda i: f"[[{i['page']}]] wraps a wikilink across lines - write it on one: [[{i['target']}]]",
|
||||
)
|
||||
_section(lines, "Orphan Pages (no inbound links)", report["orphan_pages"], lambda i: f"[[{i}]]")
|
||||
_section(
|
||||
lines, f"Most-Linked Pages (top {MOST_LINKED_COUNT} hubs)", report["most_linked"],
|
||||
@@ -849,11 +864,17 @@ def default_report_path(report: dict) -> Path:
|
||||
# it needs no migration - so failing a lint run on it would penalise an instance
|
||||
# that never asked for that platform.
|
||||
#
|
||||
# `wrapped_wikilinks` is hard from the start without tightening anything: every
|
||||
# link it names was a hard `broken_links` finding before the link graph learned
|
||||
# to fold a wrapped target, so an instance's lint is exactly as red as it was -
|
||||
# only the finding now says what is actually wrong.
|
||||
#
|
||||
# One definition, used by `lint --fail-on-error` and by the eval scorecard: if
|
||||
# the two disagreed, a run could pass its score while lint refused it.
|
||||
HARD_ERROR_KEYS = (
|
||||
"frontmatter_errors",
|
||||
"broken_links",
|
||||
"wrapped_wikilinks",
|
||||
"dangling_index_entries",
|
||||
"duplicate_titles",
|
||||
"unportable_titles",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
from chemenu.frontmatter_io import write_page
|
||||
from chemenu.kb_scan import find_nested_pages, iter_kb_pages, load_kb_pages
|
||||
from chemenu.kb_scan import (
|
||||
count_wikilinks,
|
||||
extract_wikilinks,
|
||||
find_nested_pages,
|
||||
iter_kb_pages,
|
||||
load_kb_pages,
|
||||
wrapped_wikilinks,
|
||||
)
|
||||
|
||||
|
||||
def _names(kb_dir):
|
||||
@@ -62,3 +69,24 @@ def test_find_nested_pages_flags_a_page_below_its_area(kb_dir):
|
||||
|
||||
def test_find_nested_pages_is_silent_for_pages_directly_in_an_area(kb_dir):
|
||||
assert find_nested_pages(kb_dir, load_kb_pages(kb_dir)) == []
|
||||
|
||||
|
||||
def test_a_wrapped_wikilink_reads_as_the_title_it_folds_to():
|
||||
"""A link wrapped at a fixed column captured the break as part of the
|
||||
title, so it matched no page and `lint` reported the page as missing."""
|
||||
body = "See [[Foo Bar\n Target]] and [[Foo Bar\r\nTarget|alias]] and [[Other]]."
|
||||
assert extract_wikilinks(body) == {"Foo Bar Target", "Other"}
|
||||
assert count_wikilinks(body) == {"Foo Bar Target": 2, "Other": 1}
|
||||
|
||||
|
||||
def test_wrapped_wikilinks_names_only_the_wrapped_ones():
|
||||
body = "One [[Foo Bar\n Target]], one [[Foo Bar Target]], one [[A\n\nB]]."
|
||||
assert wrapped_wikilinks(body) == ["Foo Bar Target", "A B"]
|
||||
|
||||
|
||||
def test_a_wrapped_wikilink_inside_a_fence_is_notation():
|
||||
# Only a fence can hold one: an inline code span is masked within one line
|
||||
# (see markdown_code), so a span wrapped mid-link is read as prose.
|
||||
body = "Example:\n\n```\n[[Foo Bar\n Target]]\n```\n"
|
||||
assert wrapped_wikilinks(body) == []
|
||||
assert extract_wikilinks(body) == set()
|
||||
@@ -63,6 +63,46 @@ def test_lint_detects_broken_wikilink(kb_dir):
|
||||
assert {"page": "gdeploy", "target": "Nonexistent Page"} in report["broken_links"]
|
||||
|
||||
|
||||
def _gdeploy_saying(kb_dir, prose: str) -> None:
|
||||
write_page(
|
||||
kb_dir / "entities/tools/gdeploy.md",
|
||||
{"type": "types/entity.md", "entity_type": "tool", "tags": [], "created": "2026-07-25",
|
||||
"modified": "2026-07-25", "related": [], "sources": []},
|
||||
f"\n# gdeploy\n\n## Description\n\n{prose}\n",
|
||||
)
|
||||
|
||||
|
||||
def test_a_wrapped_link_to_an_existing_page_is_wrapped_not_broken(kb_dir):
|
||||
"""The reported case: the target exists under its one-line title, and the
|
||||
finding used to read as a missing page with the line break in its name."""
|
||||
_gdeploy_saying(kb_dir, "Notes in [[Source -\n Aurora]] for details.")
|
||||
report = run_lint(kb_dir)
|
||||
assert report["broken_links"] == []
|
||||
assert report["wrapped_wikilinks"] == [{"page": "gdeploy", "target": "Source - Aurora"}]
|
||||
assert has_hard_errors(report)
|
||||
assert "wraps a wikilink across lines" in render_markdown(report)
|
||||
|
||||
|
||||
def test_a_wrapped_link_to_a_missing_page_is_both_findings(kb_dir):
|
||||
_gdeploy_saying(kb_dir, "See [[Nonexistent\n Page]] for details.")
|
||||
report = run_lint(kb_dir)
|
||||
assert {"page": "gdeploy", "target": "Nonexistent Page"} in report["broken_links"]
|
||||
assert report["wrapped_wikilinks"] == [{"page": "gdeploy", "target": "Nonexistent Page"}]
|
||||
|
||||
|
||||
def test_the_target_of_a_wrapped_link_is_not_an_orphan(kb_dir):
|
||||
assert "Source - Aurora" in run_lint(kb_dir)["orphan_pages"] # the precondition
|
||||
_gdeploy_saying(kb_dir, "Notes in [[Source -\n Aurora]] for details.")
|
||||
assert "Source - Aurora" not in run_lint(kb_dir)["orphan_pages"]
|
||||
|
||||
|
||||
def test_a_wrapped_link_inside_code_is_no_finding(kb_dir):
|
||||
_gdeploy_saying(kb_dir, "Example:\n\n```\n[[Nonexistent\n Page]]\n```")
|
||||
report = run_lint(kb_dir)
|
||||
assert report["wrapped_wikilinks"] == []
|
||||
assert report["broken_links"] == []
|
||||
|
||||
|
||||
def test_lint_fixture_has_no_dangling_frontmatter_refs(kb_dir):
|
||||
assert run_lint(kb_dir)["dangling_frontmatter_refs"] == []
|
||||
|
||||
|
||||
@@ -28,6 +28,13 @@ def test_retarget_body_ignores_partial_title_matches():
|
||||
assert page_ops.retarget_body("[[Old Thing]]", "Old", "New") == "[[Old Thing]]"
|
||||
|
||||
|
||||
def test_retarget_body_repoints_a_wrapped_link_onto_one_line():
|
||||
body = "See [[Old\n Title|the old one]] and [[Old\nTitle#Setup]]."
|
||||
result = page_ops.retarget_body(body, "Old Title", "New")
|
||||
assert result == "See [[New|the old one]] and [[New#Setup]]."
|
||||
assert page_ops.body_references(body, "Old Title") == 2
|
||||
|
||||
|
||||
def test_strip_link_bullets_removes_only_bare_bullets():
|
||||
body = (
|
||||
"## Relationships\n\n"
|
||||
@@ -214,6 +221,33 @@ def test_rm_refuses_referenced_page_without_yes(patched_wiki):
|
||||
page_ops.rm_command(page_title="Borealis", yes=False, dry_run=False)
|
||||
assert (patched_wiki / "entities/systems/Borealis.md").exists()
|
||||
|
||||
def _gdeploy_wrapping_a_link_to_source_aurora(kb) -> None:
|
||||
write_page(
|
||||
kb / "entities/tools/gdeploy.md",
|
||||
{"type": "types/entity.md", "entity_type": "tool", "tags": [],
|
||||
"created": "2026-07-25", "modified": "2026-07-25", "related": [],
|
||||
"sources": []},
|
||||
"\n# gdeploy\n\n## Description\n\nNotes in [[Source -\n Aurora]] nightly.\n",
|
||||
)
|
||||
|
||||
|
||||
def test_rm_refuses_a_page_referenced_only_by_a_wrapped_link(patched_wiki, capsys):
|
||||
"""Before the fold, the wrapped link was no reference at all, and the page
|
||||
went without the `--yes` the inbound list is there to require."""
|
||||
_gdeploy_wrapping_a_link_to_source_aurora(patched_wiki)
|
||||
with pytest.raises(typer.Exit):
|
||||
page_ops.rm_command(page_title="Source - Aurora", yes=False, dry_run=False)
|
||||
assert (patched_wiki / "sources/notes/Source - Aurora.md").exists()
|
||||
assert "- gdeploy" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_rename_repoints_a_wrapped_link(patched_wiki):
|
||||
_gdeploy_wrapping_a_link_to_source_aurora(patched_wiki)
|
||||
page_ops.rename_command(old="Source - Aurora", new="Source - Aurora Notes", dry_run=False)
|
||||
_frontmatter, body = read_page(patched_wiki / "entities/tools/gdeploy.md")
|
||||
assert "Notes in [[Source - Aurora Notes]] nightly." in body
|
||||
|
||||
|
||||
def test_rm_deletes_and_delinks_with_yes(patched_wiki):
|
||||
page_ops.rm_command(page_title="Borealis", yes=True, dry_run=False)
|
||||
|
||||
|
||||
Reference in new issue
Block a user