tinyrelayfile
tinyrelay/tinyclient/source_render_test.go/

tinyclient/source_render_test.go raw
1package tinyclient
2
3import (
4 "html/template"
5 "strings"
6 "testing"
7
8 "golang.org/x/net/html"
9)
10
11func 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}
36
37func 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}
50
51func 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}
62
63func 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}
77
78func 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}
84
85func 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}
91
92func 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.Builder
102 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 return
108 }
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}
118
119// isCodeCell reports whether a table cell holds rendered source rather than
120// a line number anchor.
121func isCodeCell(node *html.Node) bool {
122 first := node.FirstChild
123 return first != nil && first.Type == html.ElementNode && first.Data == "code"
124}
125
126func appendText(out *strings.Builder, node *html.Node) {
127 if node.Type == html.TextNode {
128 out.WriteString(node.Data)
129 return
130 }
131 for child := node.FirstChild; child != nil; child = child.NextSibling {
132 appendText(out, child)
133 }
134}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs