1package textlab23import (4 "testing"56 "gno.land/p/moul/x/daily/levenshtein/v0"7 "gno.land/p/moul/x/daily/soundex/v0"8 "gno.land/p/nt/uassert/v0"9)1011// TestRomanOf covers the one package here whose input is a number rather than12// a word, in both directions plus the two ways it can be neither.13func TestRomanOf(t *testing.T) {14 tests := []struct{ in, want string }{15 {"1987", "MCMLXXXVII"},16 {"1", "I"},17 {"3999", "MMMCMXCIX"},18 {"0", "out of range (1..3999)"},19 {"-5", "out of range (1..3999)"},20 {"4000", "out of range (1..3999)"},21 {"MCMLXXXVII", "1987 (read as roman)"},22 {"mcmlxxxvii", "1987 (read as roman)"},23 {"knight", "not a number"},24 }25 for _, tt := range tests {26 uassert.Equal(t, tt.want, romanOf(tt.in), "romanOf("+tt.in+")")27 }28}2930// TestTheDisagreement is the claim the realm is built to show, pinned in both31// directions. Measured 2026-09-23; the numbers in the package doc and on the32// page come from here, so if either package changes its mind this fails rather33// than the page quietly stating something false.34func TestTheDisagreement(t *testing.T) {35 // One edit apart, and yet soundex says unrelated: it keys on the first36 // letter, and the silent K changes it.37 uassert.Equal(t, "K523", soundex.Encode("knight"))38 uassert.Equal(t, "N230", soundex.Encode("night"))39 uassert.False(t, soundex.Match("knight", "night"), "different soundex codes")40 uassert.Equal(t, 1, levenshtein.Distance("knight", "night"))41 uassert.Equal(t, 83, levenshtein.Similarity("knight", "night"))4243 // And the other way: two edits apart, identical codes.44 uassert.Equal(t, "R163", soundex.Encode("robert"))45 uassert.Equal(t, "R163", soundex.Encode("rupert"))46 uassert.True(t, soundex.Match("robert", "rupert"), "same soundex code")47 uassert.Equal(t, 2, levenshtein.Distance("robert", "rupert"))48}4950// TestRomanOfDoesNotAbortOnOrdinaryWords is the defect this realm shipped with51// until a test found it: romannum.FromRoman PANICS on a non-numeral instead of52// returning zero, so the DEFAULT view aborted. "knight" is not a roman numeral,53// and neither is most of what anyone would put in the URL.54func TestRomanOfDoesNotAbortOnOrdinaryWords(t *testing.T) {55 for _, w := range []string{"knight", "night", "a|b", "", "hello world", "MMM!"} {56 uassert.Equal(t, "not a number", romanOf(w), "romanOf("+w+")")57 }58 uassert.True(t, isRomanNumeral("MCMLXXXVII"))59 uassert.False(t, isRomanNumeral("KNIGHT"))60 uassert.False(t, isRomanNumeral(""))61}6263// TestInputIsBounded pins the ceiling. levenshtein.Matrix is O(len(a)*len(b)),64// so an unbounded pair out of a URL is a way to make a query expensive for65// whoever serves it.66func TestInputIsBounded(t *testing.T) {67 long := ""68 for i := 0; i < maxInput+1; i++ {69 long += "x"70 }71 uassert.True(t, tooLong(long))72 uassert.False(t, tooLong("knight"))7374 out := Render(long)75 uassert.True(t, len(out) < 600, "an over-long input renders a refusal, not a matrix")76}7778// TestTableCellsAreEscaped: the input comes out of a URL and lands in a table79// cell, where an unescaped pipe eats a column.80func TestTableCellsAreEscaped(t *testing.T) {81 out := Render("a|b")82 uassert.False(t, contains(out, "| a|b |"), "a raw pipe reached a table cell")83}8485func contains(h, n string) bool {86 for i := 0; i+len(n) <= len(h); i++ {87 if h[i:i+len(n)] == n {88 return true89 }90 }91 return false92}9394// ExampleRender pins the root view, which is the default word and the "try95// two" list. Nothing here moves with the chain.96func ExampleRender() {97 print(Render(""))98 // Output:99 // # allinone: textlab100 //101 // Eight packages over one input. [source](https://mygnoscan.moul.p2p.team/realm/r/moul/x/allinone/textlab/v0)102 //103 // ## Input: knight104 //105 // | package | answer |106 // | --- | --- |107 // | `x/daily/soundex` | K523 |108 // | `x/daily/rot13` | xavtug |109 // | `x/daily/piglatin` | ightknay |110 // | `x/daily/romannum` | not a number |111 //112 // ## Try two113 //114 // - [knight vs night](/r/moul/x/allinone/textlab/v0:knight/night) (one edit apart, yet soundex says unrelated)115 // - [robert vs rupert](/r/moul/x/allinone/textlab/v0:robert/rupert) (two edits apart, yet soundex says identical)116 // - [1987 as a roman numeral](/r/moul/x/allinone/textlab/v0:1987)117}118119// ExampleRender_pair pins the comparison view, which is the realm's whole120// argument: the same pair, judged opposite ways by the two measures.121func ExampleRender_pair() {122 print(Render("knight/night"))123 // Output:124 // # allinone: textlab125 //126 // Eight packages over one input. [source](https://mygnoscan.moul.p2p.team/realm/r/moul/x/allinone/textlab/v0)127 //128 // ## Input: knight129 //130 // | package | answer |131 // | --- | --- |132 // | `x/daily/soundex` | K523 |133 // | `x/daily/rot13` | xavtug |134 // | `x/daily/piglatin` | ightknay |135 // | `x/daily/romannum` | not a number |136 //137 // ## Compared with night138 //139 // | measure | answer |140 // | --- | --- |141 // | `soundex` of knight | K523 |142 // | `soundex` of night | N230 |143 // | sound alike | no |144 // | `levenshtein` distance | 1 |145 // | `levenshtein` similarity | 83% |146}147Render(path string) string
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.