clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/ui-updates
tinyrelay / tinyclient / source_render_test.go
source_render_test.go 4.4K
1package tinyclient23import (4 "html/template"5 "strings"6 "testing"78 "golang.org/x/net/html"9)1011func TestSourceHTMLIsEscapedAndAnchored(t *testing.T) {12 got := sourceHTML(map[string]any{13 "path": "internal/main.go",14 "content": "package main\nvar x = \"<script>alert(1)</script>\"\n",15 })16 html := string(got)17 for _, want := range []string{18 `<source-view lang="go"><table>`,19 `id="L1"`,20 `href="#L2"`,21 `<b>var</b>`,22 `<q>&#34;&lt;script&gt;alert(1)&lt;/script&gt;&#34;</q>`,23 `</table></source-view>`,24 } {25 if !strings.Contains(html, want) {26 t.Fatalf("source output missing %q: %s", want, html)27 }28 }29 if strings.Contains(html, "<script>alert") || strings.Contains(html, "class=") {30 t.Fatalf("source content was not escaped or used class hooks: %s", html)31 }32 if _, ok := any(got).(template.HTML); !ok {33 t.Fatalf("sourceHTML type = %T, want template.HTML", got)34 }35}3637func TestSourceHTMLUsesPlaintextAndBoundsPreview(t *testing.T) {38 content := strings.Repeat("<unsafe>\n", sourcePreviewLimit/len("<unsafe>\n")+10)39 got := string(sourceHTML(map[string]any{"name": "README.unknown", "content": content}))40 if !strings.Contains(got, `<source-view lang="text">`) {41 t.Fatalf("unknown extension should use plaintext: %s", got[:min(len(got), 200)])42 }43 if !strings.Contains(got, `<tfoot><tr><td colspan="2">Preview truncated`) {44 t.Fatal("bounded preview should identify truncation")45 }46 if strings.Contains(got, "<unsafe>") {47 t.Fatal("plaintext source was not escaped")48 }49}5051func TestDiffHTMLClassifiesLinesWithoutHTMLInjection(t *testing.T) {52 got := string(diffHTML(map[string]any{"diff": "@@ -1 +1 @@\n-old\n+<script>bad</script>\n context"}))53 for _, want := range []string{`<diff-view><table>`, `<code><b>@@ -1 +1 @@</b></code>`, `<code><del>-old</del></code>`, `<code><ins>+&lt;script&gt;bad&lt;/script&gt;</ins></code>`, `<code> context</code>`, `</table></diff-view>`} {54 if !strings.Contains(got, want) {55 t.Fatalf("diff output missing %q: %s", want, got)56 }57 }58 if strings.Contains(got, "<script>bad") || strings.Contains(got, "class=") {59 t.Fatal("diff content was not escaped or used class hooks")60 }61}6263func TestPreviewBoundsLineMarkupAndDisclosesOmission(t *testing.T) {64 for name, render := range map[string]func(any) template.HTML{"source": sourceHTML, "diff": diffHTML} {65 t.Run(name, func(t *testing.T) {66 content := strings.Repeat("\n", sourcePreviewLimit)67 got := string(render(map[string]any{"content": content, "diff": content}))68 if len(got) > 2*1024*1024 {69 t.Fatalf("a short-line preview expanded to %d bytes", len(got))70 }71 if !strings.Contains(got, "<tfoot>") || !strings.Contains(got, "10,000 lines") {72 t.Fatal("line preview omission was not disclosed")73 }74 })75 }76}7778func TestDiffDisclosesBackendTruncation(t *testing.T) {79 got := string(diffHTML(map[string]any{"diff": "already bounded", "truncated": true}))80 if !strings.Contains(got, "<tfoot>") || !strings.Contains(got, "truncated") {81 t.Fatal("backend truncation was hidden")82 }83}8485func TestSourceDoesNotInventAnEmptyLineAtEndOfFile(t *testing.T) {86 got := string(sourceHTML(map[string]any{"content": "first\nsecond\n"}))87 if strings.Contains(got, `id="L3"`) || !strings.Contains(got, `id="L2"`) {88 t.Fatal("terminal newline added a phantom source row")89 }90}9192func TestSourceHTMLPreservesRenderedSourceText(t *testing.T) {93 want := "/* block <tag> */\nfunc π(😀 string) {\n\treturn \"<script>\"\n}\n"94 root, err := html.Parse(strings.NewReader(string(sourceHTML(map[string]any{95 "path": "unicode.go",96 "content": want,97 }))))98 if err != nil {99 t.Fatal(err)100 }101 var got strings.Builder102 var visit func(*html.Node)103 visit = func(node *html.Node) {104 if node.Type == html.ElementNode && node.Data == "td" && isCodeCell(node) {105 appendText(&got, node)106 got.WriteByte('\n')107 return108 }109 for child := node.FirstChild; child != nil; child = child.NextSibling {110 visit(child)111 }112 }113 visit(root)114 if got.String() != want {115 t.Fatalf("rendered source changed content:\nwant %q\ngot %q", want, got.String())116 }117}118119// isCodeCell reports whether a table cell holds rendered source rather than120// a line number anchor.121func isCodeCell(node *html.Node) bool {122 first := node.FirstChild123 return first != nil && first.Type == html.ElementNode && first.Data == "code"124}125126func appendText(out *strings.Builder, node *html.Node) {127 if node.Type == html.TextNode {128 out.WriteString(node.Data)129 return130 }131 for child := node.FirstChild; child != nil; child = child.NextSibling {132 appendText(out, child)133 }134}