Align topdata wiki canonical paths (#11)
Reviewed-on: https://gitea.westgate.pw/ShadowsOverWestgate/sow-tools/pulls/11 Co-authored-by: Michał Piasecki <mpiasecki720@protonmail.com> Co-committed-by: Michał Piasecki <mpiasecki720@protonmail.com>
This commit is contained in:
@@ -108,6 +108,54 @@ func TestCollectLocalPagesReadsGeneratedPageIDFromMarkerWhenPathIsTitleBased(t *
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectLocalPagesReadsCanonicalPublicPathFromPageIndex(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
sourceDir := filepath.Join(root, "pages")
|
||||
if err := os.MkdirAll(filepath.Join(sourceDir, "spells"), 0755); err != nil {
|
||||
t.Fatalf("create source dir: %v", err)
|
||||
}
|
||||
generated := `<!-- sow-topdata-wiki:page=spells:pdk:fear -->
|
||||
<!-- sow-topdata-wiki:managed:start hash="sha256:local" -->
|
||||
<h1>Fear</h1>
|
||||
<p>Generated page.</p>
|
||||
<!-- sow-topdata-wiki:managed:end -->
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(sourceDir, "spells", "Fear.html"), []byte(generated), 0644); err != nil {
|
||||
t.Fatalf("write source page: %v", err)
|
||||
}
|
||||
if err := saveWikiPageIndex(filepath.Join(root, "page-index.json"), wikiPageIndex{
|
||||
Version: wikiGeneratorVersion,
|
||||
Pages: []wikiPageIndexEntry{
|
||||
{
|
||||
PageID: "spells:pdk:fear",
|
||||
PublicPath: "Magic/Fear",
|
||||
OutputPath: "pages/spells/Fear.html",
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("write page index: %v", err)
|
||||
}
|
||||
|
||||
pages, err := collectLocalPages(sourceDir, []string{"spells"})
|
||||
if err != nil {
|
||||
t.Fatalf("collectLocalPages failed: %v", err)
|
||||
}
|
||||
if got := pages["spells:pdk:fear"].PublicPath; got != "Magic/Fear" {
|
||||
t.Fatalf("expected canonical public path from page-index, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractWikiPagePublicPathAcceptsCanonicalSlashPath(t *testing.T) {
|
||||
content := `<!-- sow-topdata-wiki:page=spells:pdk:fear public_path="Magic/Fear" -->
|
||||
<!-- sow-topdata-wiki:managed:start hash="sha256:local" -->
|
||||
<h1>Fear</h1>
|
||||
<!-- sow-topdata-wiki:managed:end -->
|
||||
`
|
||||
if got := extractWikiPagePublicPath(content); got != "Magic/Fear" {
|
||||
t.Fatalf("expected slash-separated public path, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeployWikiDryRunReadoptsMissingMappedPost(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
sourceDir := filepath.Join(root, "pages")
|
||||
@@ -200,6 +248,83 @@ func TestMatchExistingNodeBBPageIgnoresRetiredGeneratedPublicSlug(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchExistingNodeBBPageDoesNotPreferRetiredGeneratedSlugWhenRemoteOrderChanges(t *testing.T) {
|
||||
page := wikiDeployPage{
|
||||
PageID: "spells:pdk:fear",
|
||||
Title: "Fear",
|
||||
PublicPath: "Spells/Fear",
|
||||
}
|
||||
matched, ok := matchExistingNodeBBPage(page, []nodeBBWikiPage{
|
||||
{TID: 32, Title: "Fear", TitleLeaf: "Fear", Slug: "32/pdk-fear", WikiPath: "/wiki/spells/pdk-fear"},
|
||||
{TID: 31, Title: "Fear", TitleLeaf: "Fear", Slug: "31/fear"},
|
||||
})
|
||||
if !ok || matched.TID != 31 {
|
||||
t.Fatalf("expected canonical leaf match to select tid 31 over retired generated slug, got %#v ok=%t", matched, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchExistingNodeBBPageAdoptsCanonicalWikiPathWhenSlugIsRetired(t *testing.T) {
|
||||
page := wikiDeployPage{
|
||||
PageID: "spells:pdk:fear",
|
||||
Title: "Fear",
|
||||
}
|
||||
matched, ok := matchExistingNodeBBPage(page, []nodeBBWikiPage{
|
||||
{TID: 32, Title: "Fear", TitleLeaf: "Fear", Slug: "32/pdk-fear", WikiPath: "/wiki/Spells/Fear"},
|
||||
})
|
||||
if !ok || matched.TID != 32 {
|
||||
t.Fatalf("expected title fallback to adopt canonical wiki path despite retired NodeBB topic slug, got %#v ok=%t", matched, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchExistingNodeBBPageRequiresFullPathBeforeLeafFallback(t *testing.T) {
|
||||
page := wikiDeployPage{
|
||||
PageID: "spells:pdk:fear",
|
||||
Title: "Fear",
|
||||
PublicPath: "Spells/Necromancy/Fear",
|
||||
}
|
||||
matched, ok := matchExistingNodeBBPage(page, []nodeBBWikiPage{
|
||||
{TID: 31, Title: "Fear", TitleLeaf: "Fear", WikiPath: "/wiki/Spells/Illusion/Fear"},
|
||||
})
|
||||
if ok {
|
||||
t.Fatalf("expected no cross-namespace leaf fallback match, got %#v", matched)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchExistingNodeBBPageRequiresCanonicalPathForNestedPublicPath(t *testing.T) {
|
||||
page := wikiDeployPage{
|
||||
PageID: "spells:pdk:fear",
|
||||
Title: "Fear",
|
||||
PublicPath: "Spells/Necromancy/Fear",
|
||||
}
|
||||
matched, ok := matchExistingNodeBBPage(page, []nodeBBWikiPage{
|
||||
{TID: 31, Title: "Fear", TitleLeaf: "Fear", Slug: "31/fear"},
|
||||
})
|
||||
if ok {
|
||||
t.Fatalf("expected no nested-path title or leaf fallback match without canonical path, got %#v", matched)
|
||||
}
|
||||
|
||||
matched, ok = matchExistingNodeBBPage(page, []nodeBBWikiPage{
|
||||
{TID: 31, Title: "Fear", TitleLeaf: "Fear", CanonicalPath: "Spells/Necromancy/Fear"},
|
||||
})
|
||||
if !ok || matched.TID != 31 {
|
||||
t.Fatalf("expected canonical path match for nested public path, got %#v ok=%t", matched, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNestedCanonicalAdoptionFallbackError(t *testing.T) {
|
||||
page := wikiDeployPage{
|
||||
PageID: "spells:pdk:fear",
|
||||
Title: "Fear",
|
||||
PublicPath: "Spells/Necromancy/Fear",
|
||||
}
|
||||
err := nestedCanonicalAdoptionFallbackError(page, []nodeBBWikiPage{
|
||||
{TID: 31, Title: "Fear", TitleLeaf: "Fear", Slug: "31/fear"},
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "requires exact canonical wikiPath/canonicalPath") {
|
||||
t.Fatalf("expected exact canonical remapping error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeployCanonicalWikiSegmentMatchesGeneratedPathPolicy(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
`Grandmaster's Battle Momentum`: "Grandmasters_Battle_Momentum",
|
||||
@@ -217,6 +342,90 @@ func TestDeployCanonicalWikiSegmentMatchesGeneratedPathPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadWikiNamespaceDeclarationsValidatesCanonicalPaths(t *testing.T) {
|
||||
root := testProjectRoot(t)
|
||||
wikiDir := filepath.Join(root, "topdata", "wiki")
|
||||
if err := os.MkdirAll(wikiDir, 0755); err != nil {
|
||||
t.Fatalf("create wiki dir: %v", err)
|
||||
}
|
||||
write := func(text string) {
|
||||
t.Helper()
|
||||
if err := os.WriteFile(filepath.Join(wikiDir, "namespaces.yaml"), []byte(text), 0644); err != nil {
|
||||
t.Fatalf("write namespaces: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
write(`
|
||||
namespaces:
|
||||
- id: feat
|
||||
title: Feats
|
||||
canonical_path: Wiki/Feats
|
||||
category_env: SOW_WIKI_FEATS_CID
|
||||
edit_policy: preserve_manual_sections
|
||||
`)
|
||||
declarations, err := loadWikiNamespaceDeclarations(testProject(root))
|
||||
if err != nil {
|
||||
t.Fatalf("expected canonical namespace path declaration to load: %v", err)
|
||||
}
|
||||
if got := declarations[0].CanonicalPath; got != "Wiki/Feats" {
|
||||
t.Fatalf("expected canonical namespace path to be preserved, got %q", got)
|
||||
}
|
||||
|
||||
write(`
|
||||
namespaces:
|
||||
- id: search
|
||||
title: Search
|
||||
canonical_path: Wiki/Search
|
||||
category_env: SOW_WIKI_SEARCH_CID
|
||||
edit_policy: preserve_manual_sections
|
||||
`)
|
||||
declarations, err = loadWikiNamespaceDeclarations(testProject(root))
|
||||
if err != nil {
|
||||
t.Fatalf("expected reserved display title with safe canonical path to load: %v", err)
|
||||
}
|
||||
if got := declarations[0].CanonicalPath; got != "Wiki/Search" {
|
||||
t.Fatalf("expected safe canonical namespace path to be preserved, got %q", got)
|
||||
}
|
||||
|
||||
write(`
|
||||
namespaces:
|
||||
- id: bad
|
||||
title: "!!!"
|
||||
category_env: SOW_WIKI_BAD_CID
|
||||
edit_policy: preserve_manual_sections
|
||||
`)
|
||||
_, err = loadWikiNamespaceDeclarations(testProject(root))
|
||||
if err == nil || !strings.Contains(err.Error(), `topdata wiki namespace "bad" title`) {
|
||||
t.Fatalf("expected invalid namespace title error, got %v", err)
|
||||
}
|
||||
|
||||
write(`
|
||||
namespaces:
|
||||
- id: search
|
||||
title: Search
|
||||
canonical_path: search
|
||||
category_env: SOW_WIKI_SEARCH_CID
|
||||
edit_policy: preserve_manual_sections
|
||||
`)
|
||||
_, err = loadWikiNamespaceDeclarations(testProject(root))
|
||||
if err == nil || !strings.Contains(err.Error(), `reserved first segment`) {
|
||||
t.Fatalf("expected reserved namespace path error, got %v", err)
|
||||
}
|
||||
|
||||
write(`
|
||||
namespaces:
|
||||
- id: numeric
|
||||
title: Feats
|
||||
canonical_path: 123/Feats
|
||||
category_env: SOW_WIKI_NUMERIC_CID
|
||||
edit_policy: preserve_manual_sections
|
||||
`)
|
||||
_, err = loadWikiNamespaceDeclarations(testProject(root))
|
||||
if err == nil || !strings.Contains(err.Error(), `reserved first segment`) {
|
||||
t.Fatalf("expected numeric namespace path error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeployWikiCreatesNodeBBTopicAndWritesManifest(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
sourceDir := filepath.Join(root, "pages")
|
||||
|
||||
Reference in New Issue
Block a user