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:
@@ -180,6 +180,65 @@ func TestBuildWikiRegeneratesMissingCachedPages(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildNativeIndexesUnregisteredStatusPagesWithDeclaredNamespacePath(t *testing.T) {
|
||||
root := testProjectRoot(t)
|
||||
mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
|
||||
mkdirAll(t, filepath.Join(root, "topdata", "wiki"))
|
||||
writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
|
||||
writeFile(t, filepath.Join(root, "topdata", "wiki", "namespaces.yaml"), `
|
||||
namespaces:
|
||||
- id: meta
|
||||
title: Wiki Status
|
||||
canonical_path: Wiki/Status
|
||||
category_env: SOW_WIKI_META_CID
|
||||
edit_policy: generated_only
|
||||
`)
|
||||
writeFile(t, filepath.Join(root, "topdata", "data", "skills", "base.json"), `{
|
||||
"output": "skills.2da",
|
||||
"columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"],
|
||||
"rows": [
|
||||
{
|
||||
"id": 0,
|
||||
"key": "skills:athletics",
|
||||
"Label": "Athletics",
|
||||
"Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}},
|
||||
"Description": {"tlk": {"key": "skills:athletics.description", "text": "Ability: Strength."}},
|
||||
"HideFromLevelUp": "0",
|
||||
"Untrained": "1",
|
||||
"KeyAbility": "STR",
|
||||
"ArmorCheckPenalty": "1",
|
||||
"Constant": "SKILL_ATHLETICS"
|
||||
}
|
||||
]
|
||||
}`+"\n")
|
||||
writeFile(t, filepath.Join(root, "topdata", "data", "skills", "lock.json"), `{"skills:athletics":0}`+"\n")
|
||||
|
||||
proj := testProject(root)
|
||||
proj.Config.TopData.ReferenceBuilder = ""
|
||||
|
||||
if _, err := BuildNative(proj, nil); err != nil {
|
||||
t.Fatalf("BuildNative failed: %v", err)
|
||||
}
|
||||
indexRaw, err := os.ReadFile(filepath.Join(root, ".cache", "wiki", "page-index.json"))
|
||||
if err != nil {
|
||||
t.Fatalf("read page index: %v", err)
|
||||
}
|
||||
var pageIndex wikiPageIndex
|
||||
if err := json.Unmarshal(indexRaw, &pageIndex); err != nil {
|
||||
t.Fatalf("parse page index: %v", err)
|
||||
}
|
||||
paths := map[string]string{}
|
||||
for _, page := range pageIndex.Pages {
|
||||
paths[page.PageID] = page.PublicPath
|
||||
}
|
||||
if got, want := paths["meta:wikistatus"], "Wiki/Status/Wiki_Status"; got != want {
|
||||
t.Fatalf("expected declared canonical path for status page, got %q want %q", got, want)
|
||||
}
|
||||
if got := paths["meta:wikistatus:skills:vanilla"]; !strings.HasPrefix(got, "Wiki/Status/") {
|
||||
t.Fatalf("expected declared canonical path for status listing, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildNativeCanOmitAggregateWikiStatusListings(t *testing.T) {
|
||||
root := testProjectRoot(t)
|
||||
mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
|
||||
@@ -1367,6 +1426,29 @@ func TestSaveWikiPageIndexRejectsDuplicatePublicPaths(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveWikiPageIndexRejectsFoldedDuplicatePublicPaths(t *testing.T) {
|
||||
err := saveWikiPageIndex(filepath.Join(t.TempDir(), "page-index.json"), wikiPageIndex{
|
||||
Version: wikiGeneratorVersion,
|
||||
Pages: []wikiPageIndexEntry{
|
||||
{PageID: "spells:fear", PublicPath: "Magic/Fear", OutputPath: "pages/spells/fear.html"},
|
||||
{PageID: "spells:accented_fear", PublicPath: "magic/fear", OutputPath: "pages/spells/accented_fear.html"},
|
||||
},
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "duplicate wiki page-index public_path") {
|
||||
t.Fatalf("expected folded duplicate public_path validation error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicWikiPathSplitsTitlePathWithoutWhitespaceAroundSeparator(t *testing.T) {
|
||||
ctx := &wikiContext{namespaceTitles: map[string]string{"spells": "Magic"}}
|
||||
if got := ctx.publicWikiPath("spells", "Parent::Child"); got != "Magic/Parent/Child" {
|
||||
t.Fatalf("expected NodeBB-style title path split, got %q", got)
|
||||
}
|
||||
if got := wikiPageOutputRelPath("spells:parent_child", "Parent::Child"); got != filepath.FromSlash("spells/Parent/Child.html") {
|
||||
t.Fatalf("expected title-based output path split, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWikiPagePathDeclarationsRejectsLegacySlugOverrides(t *testing.T) {
|
||||
_, err := parseWikiPagePathDeclarations([]byte(`
|
||||
page_paths:
|
||||
@@ -1401,16 +1483,95 @@ func TestCanonicalWikiSegmentPreservesCaseAndUnderscoreSpaces(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalWikiSegmentFoldedKeyMatchesPluginFixture(t *testing.T) {
|
||||
tests := []struct {
|
||||
source string
|
||||
canonical string
|
||||
folded string
|
||||
}{
|
||||
{source: "Inspire Competence", canonical: "Inspire_Competence", folded: "inspire competence"},
|
||||
{source: "Grandmaster's Battle Momentum", canonical: "Grandmasters_Battle_Momentum", folded: "grandmasters battle momentum"},
|
||||
{source: "Æther Œuvre Øresund Straße Þorn Łódź Đelta", canonical: "Aether_Oeuvre_Oresund_Strasse_Thorn_Lodz_Delta", folded: "aether oeuvre oresund strasse thorn lodz delta"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := canonicalWikiSegment(tt.source); got != tt.canonical {
|
||||
t.Fatalf("canonicalWikiSegment(%q) = %q, want %q", tt.source, got, tt.canonical)
|
||||
}
|
||||
if got := canonicalWikiSegmentFoldedKey(tt.source); got != tt.folded {
|
||||
t.Fatalf("canonicalWikiSegmentFoldedKey(%q) = %q, want %q", tt.source, got, tt.folded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWikiStatusPagesEmitCanonicalLinks(t *testing.T) {
|
||||
ctx := &wikiContext{}
|
||||
report := renderWikiStatusReportPage(map[string]map[string]any{
|
||||
"skills": {
|
||||
"status": wikiStatusVanilla,
|
||||
"new": 0,
|
||||
"modified": 0,
|
||||
"vanilla": 1,
|
||||
"total": 1,
|
||||
},
|
||||
}, []string{"skills"}, ctx)
|
||||
if !strings.Contains(report, "[[Skills|Skills]]") || strings.Contains(report, "[[skills:index|Skills]]") {
|
||||
t.Fatalf("expected status report namespace link to use canonical public path, got:\n%s", report)
|
||||
}
|
||||
|
||||
listing := renderWikiStatusListingPage("Skills Vanilla Pages", []string{"skills:inspire_competence", "feat:grandmaster_battle_momentum"}, map[string]string{
|
||||
"skills:inspire_competence": "Inspire Competence",
|
||||
"feat:grandmaster_battle_momentum": "Grandmaster's Battle Momentum",
|
||||
}, ctx)
|
||||
for _, want := range []string{
|
||||
"[[Skills/Inspire_Competence|Inspire Competence]]",
|
||||
"[[Feats/Grandmasters_Battle_Momentum|Grandmaster's Battle Momentum]]",
|
||||
} {
|
||||
if !strings.Contains(listing, want) {
|
||||
t.Fatalf("expected status listing canonical link %q, got:\n%s", want, listing)
|
||||
}
|
||||
}
|
||||
if strings.Contains(listing, "[[skills:inspire_competence|") || strings.Contains(listing, "[[feat:grandmaster_battle_momentum|") {
|
||||
t.Fatalf("expected status listing to omit generated page IDs as public targets, got:\n%s", listing)
|
||||
}
|
||||
|
||||
customCtx := &wikiContext{
|
||||
namespaceTitles: map[string]string{
|
||||
"spells": "Magic",
|
||||
},
|
||||
}
|
||||
customReport := renderWikiStatusReportPage(map[string]map[string]any{
|
||||
"spells": {
|
||||
"status": wikiStatusNew,
|
||||
"new": 1,
|
||||
"modified": 0,
|
||||
"vanilla": 0,
|
||||
"total": 1,
|
||||
},
|
||||
}, []string{"spells"}, customCtx)
|
||||
if !strings.Contains(customReport, "[[Magic|Magic]]") || strings.Contains(customReport, "[[Spells|Spells]]") {
|
||||
t.Fatalf("expected custom namespace status report link to use declared canonical title, got:\n%s", customReport)
|
||||
}
|
||||
customListing := renderWikiStatusListingPage("Magic New Pages", []string{"spells:fear"}, map[string]string{
|
||||
"spells:fear": "Fear",
|
||||
}, customCtx)
|
||||
if !strings.Contains(customListing, "[[Magic/Fear|Fear]]") || strings.Contains(customListing, "[[Spells/Fear|Fear]]") {
|
||||
t.Fatalf("expected custom namespace status listing link to use declared canonical title, got:\n%s", customListing)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicWikiTargetUsesCanonicalNamespaceAndTitlePath(t *testing.T) {
|
||||
ctx := &wikiContext{
|
||||
namespaceTitles: map[string]string{
|
||||
"feat": "Feats",
|
||||
},
|
||||
namespacePaths: map[string]string{
|
||||
"feat": "Wiki/Feats",
|
||||
},
|
||||
rowsByKey: map[string]map[string]any{
|
||||
"feat:hardiness_versus_enchantments": {"FEAT": map[string]any{"tlk": map[string]any{"text": "Hardiness vs. Enchantments"}}},
|
||||
},
|
||||
}
|
||||
if got, want := ctx.publicWikiTargetForKey("feat:hardiness_versus_enchantments", "Display Alias"), "Feats/Hardiness_vs_Enchantments"; got != want {
|
||||
if got, want := ctx.publicWikiTargetForKey("feat:hardiness_versus_enchantments", "Display Alias"), "Wiki/Feats/Hardiness_vs_Enchantments"; got != want {
|
||||
t.Fatalf("expected canonical title path %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user