PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/wiki/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / wiki
Files
18 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/wiki/v0
gno
0.9

Files (18)

  • README.mdmarkdown
  • gnomod.tomltoml
  • diff.gnogno
  • doc.gnogno
  • links.gnogno
  • page.gnogno
  • render.gnogno
  • revision.gnogno
  • talk.gnogno
  • title.gnogno
  • wiki.gnogno
  • diff_test.gnogno
  • limits_test.gnogno
  • links_test.gnogno
  • render_test.gnogno
  • talk_test.gnogno
  • title_test.gnogno
  • wiki_test.gnogno
  • title_test.gnogno
    1package wiki23import (4	"testing"56	"gno.land/p/nt/markdown/sanitize/v0"7	"gno.land/p/nt/uassert/v0"8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    )
    9
    10func TestParseTitle(t *testing.T) {
    11 cases := []struct {
    12 name string
    13 in string
    14 wantNS Namespace
    15 want string // canonical String(), "" when an error is expected
    16 wantErr error
    17 }{
    18 {"plain", "Gno land", NSMain, "Gno land", nil},
    19 {"underscores become spaces", "Gno_land", NSMain, "Gno land", nil},
    20 {"whitespace collapses", " Gno land ", NSMain, "Gno land", nil},
    21 {"first letter is upper-cased", "gno land", NSMain, "Gno land", nil},
    22 {"namespace prefix", "Help:Gno land", NSHelp, "Help:Gno land", nil},
    23 {"namespace prefix is case-insensitive", "help:gno_land", NSHelp, "Help:Gno land", nil},
    24 {"category", "Category:Chains", NSCategory, "Category:Chains", nil},
    25 {"unknown prefix stays in the name", "Foo:Bar", NSMain, "Foo:Bar", nil},
    26 {"unicode letters are allowed", "Élysée", NSMain, "Élysée", nil},
    27 {"unicode is upper-cased too", "élysée", NSMain, "Élysée", nil},
    28 {"zero-width characters are stripped", "Gno​land", NSMain, "Gnoland", nil},
    29 {"empty", "", NSMain, "", ErrEmptyTitle},
    30 {"whitespace only", " ", NSMain, "", ErrEmptyTitle},
    31 {"namespace with no name", "Help:", NSMain, "", ErrEmptyTitle},
    32 {"slash is rejected", "Gno/land", NSMain, "", ErrTitleChar},
    33 {"pipe is rejected", "Gno|land", NSMain, "", ErrTitleChar},
    34 {"markdown star is rejected", "Gno*land", NSMain, "", ErrTitleChar},
    35 {"bracket is rejected", "Gno[land", NSMain, "", ErrTitleChar},
    36 {"hash is rejected", "Gno#land", NSMain, "", ErrTitleChar},
    37 }
    38 for _, tc := range cases {
    39 got, err := ParseTitle(tc.in)
    40 if tc.wantErr != nil {
    41 uassert.ErrorIs(t, err, tc.wantErr, tc.name)
    42 continue
    43 }
    44 if !uassert.NoError(t, err, tc.name) {
    45 continue
    46 }
    47 uassert.Equal(t, tc.want, got.String(), tc.name)
    48 uassert.Equal(t, uint64(tc.wantNS), uint64(got.NS), tc.name+" (namespace)")
    49 }
    50}
    51
    52func TestTitleTooLong(t *testing.T) {
    53 long := ""
    54 for i := 0; i <= MaxTitleLen; i++ {
    55 long += "a"
    56 }
    57 _, err := ParseTitle(long)
    58 uassert.ErrorIs(t, err, ErrTitleTooLong)
    59}
    60
    61func TestTitleKeyOrdersByNamespace(t *testing.T) {
    62 // The avl key must group namespaces, and must keep doing so past nine
    63 // namespaces, which is why the code is a fixed-width string rather than
    64 // a ufmt-formatted number.
    65 uassert.Equal(t, "00:Gno land", MustParseTitle("Gno land").Key())
    66 uassert.Equal(t, "01:Gno land", MustParseTitle("User:Gno land").Key())
    67 uassert.True(t, MustParseTitle("Gno land").Key() < MustParseTitle("User:Alpha").Key(),
    68 "every main-namespace key must sort before every User key")
    69}
    70
    71func TestTitleSlug(t *testing.T) {
    72 uassert.Equal(t, "Gno_land", MustParseTitle("Gno land").Slug())
    73 uassert.Equal(t, "User:Gno_land", MustParseTitle("User:Gno land").Slug())
    74 uassert.Equal(t, "Mercury_(planet)", MustParseTitle("Mercury (planet)").Slug())
    75}
    76
    77// TestTitleSurvivesTheRenderPipeline pins the coupling between the title
    78// charset and the markdown sanitizer.
    79//
    80// Rendering escapes the whole body first and rewrites wikilinks second, so
    81// every title character has to survive that round trip: sanitize escapes it,
    82// ScanLinks finds the link in the escaped text, and unescapeInline hands
    83// ParseTitle the bytes the author typed. The sanitizer escapes with
    84// backslashes ("(" becomes "\(") which unescapeInline undoes, but a character
    85// escaped as an entity ("&" becomes "&amp;") would not survive, which is why
    86// the charset excludes those. This test fails the day either side changes.
    87func TestTitleSurvivesTheRenderPipeline(t *testing.T) {
    88 // The ASCII allowlist plus a couple of accepted non-ASCII letters; the
    89 // whole set has to fit in one title, so this is not every valid rune.
    90 chars := "Élysée東京"
    91 for r := rune(0); r < 0x80; r++ {
    92 if validTitleRune(r) && r != ' ' {
    93 chars += string(r)
    94 }
    95 }
    96 want := MustParseTitle(chars)
    97
    98 escaped := sanitize.BlockRich("[[" + chars + "]]\n")
    99 links := ScanLinks(escaped, `\[\[`, `\]\]`)
    100 if !uassert.Equal(t, 1, len(links), "the escaped body must still contain exactly one link") {
    101 return
    102 }
    103 got, err := ParseTitle(unescapeInline(links[0].Target))
    104 uassert.NoError(t, err)
    105 uassert.Equal(t, want.String(), got.String())
    106}
    107