Match wiki pages by address so names with spaces are the same page #64
@@ -142,9 +142,27 @@ jobs:
|
||||
return owner, repo
|
||||
|
||||
|
||||
def page_key(title):
|
||||
"""Return the identity of a page: Gitea stores a space as a hyphen.
|
||||
|
||||
The page "Home da" is the file Home-da.md and its address is Home-da, so
|
||||
both spellings must name the same page.
|
||||
"""
|
||||
return str(title).replace(" ", "-")
|
||||
|
||||
|
||||
def read_pages(wiki_dir):
|
||||
"""Return {page title: content} for every Markdown file in wiki_dir."""
|
||||
pages = {path.stem: path.read_bytes() for path in sorted(wiki_dir.glob("*.md"))}
|
||||
"""Return {page key: content} for every Markdown file in wiki_dir."""
|
||||
pages = {}
|
||||
for path in sorted(wiki_dir.glob("*.md")):
|
||||
key = page_key(path.stem)
|
||||
if key in pages:
|
||||
raise WorkflowError(
|
||||
"configuration",
|
||||
f"Two files in {wiki_dir} name the wiki page {key}; a space and "
|
||||
"a hyphen are the same in a page name.",
|
||||
)
|
||||
pages[key] = path.read_bytes()
|
||||
if not pages:
|
||||
# Publishing an empty directory would delete the whole wiki.
|
||||
raise WorkflowError(
|
||||
@@ -154,7 +172,10 @@ jobs:
|
||||
|
||||
|
||||
def list_wiki_titles(wiki_url, headers, send):
|
||||
"""Return the titles of the pages that exist in the wiki.
|
||||
"""Return the keys of the pages that exist in the wiki.
|
||||
|
||||
The key is the address Gitea reports (sub_url), or the title with its
|
||||
spaces replaced by hyphens.
|
||||
|
||||
Gitea answers 404 for a wiki that has never been written to; that is an
|
||||
empty wiki, not a failure.
|
||||
@@ -171,7 +192,11 @@ jobs:
|
||||
raise
|
||||
if not isinstance(batch, list):
|
||||
raise WorkflowError("api", "Gitea returned an invalid wiki page list.")
|
||||
titles.update(str(item.get("title", "")) for item in batch)
|
||||
titles.update(
|
||||
urllib.parse.unquote(str(item.get("sub_url") or ""))
|
||||
or page_key(item.get("title", ""))
|
||||
for item in batch
|
||||
)
|
||||
if len(batch) < PAGE_LIMIT:
|
||||
return titles
|
||||
page += 1
|
||||
|
||||
@@ -67,6 +67,27 @@ class TestReadPages:
|
||||
publish_wiki.read_pages(tmp_path)
|
||||
|
||||
|
||||
class TestPageKey:
|
||||
@pytest.mark.parametrize(
|
||||
("name", "key"),
|
||||
[("Home", "Home"), ("Home da", "Home-da"), ("Home-da", "Home-da"), ("Home.da", "Home.da"),
|
||||
("AI Prompt Template ms", "AI-Prompt-Template-ms"), ("_Sidebar", "_Sidebar")],
|
||||
)
|
||||
def test_a_space_and_a_hyphen_are_the_same_page(self, publish_wiki, name, key):
|
||||
assert publish_wiki.page_key(name) == key
|
||||
|
||||
def test_file_names_with_spaces_and_hyphens_are_keyed_the_same(self, publish_wiki, tmp_path):
|
||||
(tmp_path / "Home da.md").write_bytes(b"hjem")
|
||||
(tmp_path / "_Sidebar.md").write_bytes(b"menu")
|
||||
assert publish_wiki.read_pages(tmp_path) == {"Home-da": b"hjem", "_Sidebar": b"menu"}
|
||||
|
||||
def test_two_files_for_one_page_are_refused(self, publish_wiki, tmp_path):
|
||||
(tmp_path / "Home da.md").write_bytes(b"one")
|
||||
(tmp_path / "Home-da.md").write_bytes(b"two")
|
||||
with pytest.raises(publish_wiki.WorkflowError, match="name the wiki page Home-da"):
|
||||
publish_wiki.read_pages(tmp_path)
|
||||
|
||||
|
||||
class FakeGitea:
|
||||
"""Answers like the Gitea repository and wiki endpoints."""
|
||||
|
||||
@@ -84,7 +105,7 @@ class FakeGitea:
|
||||
if url.startswith(f"{WIKI_URL}/pages"):
|
||||
if not self.wiki_initialised:
|
||||
raise self.error("configuration", "404")
|
||||
return [{"title": title} for title in self.wiki]
|
||||
return [{"title": key.replace("-", " "), "sub_url": key} for key in self.wiki]
|
||||
if method == "POST" and url == f"{WIKI_URL}/new":
|
||||
self.wiki[body["title"]] = body["content_base64"]
|
||||
return None
|
||||
@@ -265,6 +286,64 @@ class TestPublish:
|
||||
assert gitea.wiki == {"Home": b64("keep me")}
|
||||
|
||||
|
||||
class TestPageIdentity:
|
||||
"""A wiki that already has pages named with spaces, as Gitea stores them."""
|
||||
|
||||
def test_a_file_named_with_a_space_updates_the_existing_page(
|
||||
self, publish_wiki, environment
|
||||
):
|
||||
(environment / "Home.md").unlink()
|
||||
(environment / "Credentials.md").unlink()
|
||||
(environment / ".publish").write_text("", encoding="utf-8")
|
||||
(environment / "Home da.md").write_text("hjem v2", encoding="utf-8")
|
||||
gitea = FakeGitea(publish_wiki, wiki={"Home-da": b64("hjem v1")})
|
||||
|
||||
assert run_main(publish_wiki, gitea) == 0
|
||||
|
||||
assert gitea.wiki == {"Home-da": b64("hjem v2")}
|
||||
assert [call[0] for call in gitea.calls].count("POST") == 0
|
||||
assert ("PATCH", f"{WIKI_URL}/page/Home-da") in [(c[0], c[1]) for c in gitea.calls]
|
||||
|
||||
def test_identical_pages_with_spaces_are_a_no_op(self, publish_wiki, environment):
|
||||
(environment / "Home.md").unlink()
|
||||
(environment / "Credentials.md").unlink()
|
||||
(environment / "Home da.md").write_text("hjem", encoding="utf-8")
|
||||
(environment / "_Sidebar.md").write_text("menu", encoding="utf-8")
|
||||
gitea = FakeGitea(
|
||||
publish_wiki, wiki={"Home-da": b64("hjem"), "_Sidebar": b64("menu")}
|
||||
)
|
||||
|
||||
assert run_main(publish_wiki, gitea) == 0
|
||||
|
||||
assert gitea.writes == []
|
||||
|
||||
def test_prune_does_not_delete_a_page_that_is_in_the_folder_under_another_spelling(
|
||||
self, publish_wiki, environment
|
||||
):
|
||||
(environment / "Home.md").unlink()
|
||||
(environment / "Credentials.md").unlink()
|
||||
(environment / "Install ms.md").write_text("pasang", encoding="utf-8")
|
||||
gitea = FakeGitea(
|
||||
publish_wiki, wiki={"Install-ms": b64("pasang"), "Old-page": b64("x")}
|
||||
)
|
||||
|
||||
assert run_main(publish_wiki, gitea) == 0
|
||||
|
||||
assert gitea.wiki == {"Install-ms": b64("pasang")}
|
||||
|
||||
def test_the_address_wins_over_the_title_and_is_decoded(self, publish_wiki):
|
||||
def listing(url, headers=None, **kwargs):
|
||||
return [
|
||||
{"title": "Whatever", "sub_url": "Real-Address"},
|
||||
{"title": "Only title"},
|
||||
{"title": "Encoded", "sub_url": "Caf%C3%A9"},
|
||||
]
|
||||
|
||||
keys = publish_wiki.list_wiki_titles(WIKI_URL, {}, listing)
|
||||
|
||||
assert keys == {"Real-Address", "Only-title", "Café"}
|
||||
|
||||
|
||||
class TestDescribeToken:
|
||||
def test_recognises_a_gitea_token_shape(self, publish_wiki):
|
||||
description = publish_wiki.describe_token("a1b2c3d4e5" * 4)
|
||||
|
||||
Reference in New Issue
Block a user