orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1package main
2
3import (
4 "archive/zip"
5 "bytes"
6 "strings"
7 "testing"
8)
9
10func zipOf(t *testing.T, parts map[string]string) []byte {
11 t.Helper()
12 var buf bytes.Buffer
13 w := zip.NewWriter(&buf)
14 for name, body := range parts {
15 f, err := w.Create(name)
16 if err != nil {
17 t.Fatal(err)
18 }
19 if _, err := f.Write([]byte(body)); err != nil {
20 t.Fatal(err)
21 }
22 }
23 if err := w.Close(); err != nil {
24 t.Fatal(err)
25 }
26 return buf.Bytes()
27}
28
29func TestDocxKeepsParagraphsAndTableRows(t *testing.T) {
30 raw := zipOf(t, map[string]string{"word/document.xml": `<?xml version="1.0"?>
31<w:document xmlns:w="x"><w:body>
32<w:p><w:r><w:t>First line</w:t></w:r></w:p>
33<w:p><w:r><w:t>Second </w:t></w:r><w:r><w:t>line</w:t></w:r></w:p>
34<w:tbl><w:tr><w:tc><w:p><w:r><w:t>a</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t>b</w:t></w:r></w:p></w:tc></w:tr></w:tbl>
35</w:body></w:document>`})
36
37 text, kind, err := officeText(raw)
38 if err != nil {
39 t.Fatal(err)
40 }
41 if kind != "docx" {
42 t.Errorf("kind = %q", kind)
43 }
44 // Runs inside one paragraph join, rather than becoming two lines.
45 if !strings.Contains(text, "Second line") {
46 t.Errorf("runs did not join:\n%s", text)
47 }
48 if !strings.Contains(text, "First line") {
49 t.Errorf("missing text:\n%s", text)
50 }
51}
52
53// The shared string table is the thing that makes a spreadsheet unreadable when
54// missed, since every label is an index and not a value.
55func TestXlsxResolvesSharedStrings(t *testing.T) {
56 raw := zipOf(t, map[string]string{
57 "xl/workbook.xml": `<workbook/>`,
58 "xl/sharedStrings.xml": `<?xml version="1.0"?>
59<sst><si><t>date</t></si><si><t>amount</t></si><si><t>coffee</t></si></sst>`,
60 "xl/worksheets/sheet1.xml": `<?xml version="1.0"?>
61<worksheet><sheetData>
62<row r="1"><c r="A1" t="s"><v>0</v></c><c r="B1" t="s"><v>1</v></c></row>
63<row r="2"><c r="A2" t="s"><v>2</v></c><c r="B2"><v>4.75</v></c></row>
64</sheetData></worksheet>`,
65 })
66
67 text, kind, err := officeText(raw)
68 if err != nil {
69 t.Fatal(err)
70 }
71 if kind != "xlsx" {
72 t.Errorf("kind = %q", kind)
73 }
74 if !strings.Contains(text, "date\tamount") {
75 t.Errorf("header row wrong:\n%s", text)
76 }
77 if !strings.Contains(text, "coffee\t4.75") {
78 t.Errorf("a shared string or a number was lost:\n%s", text)
79 }
80 // An index must never reach the model as a bare number.
81 if strings.Contains(text, "0\t1") {
82 t.Errorf("shared string indexes leaked:\n%s", text)
83 }
84}
85
86func TestPptxOrdersSlidesNumerically(t *testing.T) {
87 slide := func(s string) string {
88 return `<?xml version="1.0"?><p:sld xmlns:a="x"><p:cSld><a:p><a:r><a:t>` + s + `</a:t></a:r></a:p></p:cSld></p:sld>`
89 }
90 raw := zipOf(t, map[string]string{
91 "ppt/slides/slide1.xml": slide("one"),
92 "ppt/slides/slide2.xml": slide("two"),
93 "ppt/slides/slide10.xml": slide("ten"),
94 })
95 text, kind, err := officeText(raw)
96 if err != nil {
97 t.Fatal(err)
98 }
99 if kind != "pptx" {
100 t.Errorf("kind = %q", kind)
101 }
102 // slide10 sorts before slide2 as a string, which is the trap.
103 if strings.Index(text, "two") > strings.Index(text, "ten") {
104 t.Errorf("slides came out in string order:\n%s", text)
105 }
106}
107
108func TestAPlainZipIsRefusedWithAdvice(t *testing.T) {
109 raw := zipOf(t, map[string]string{"notes.txt": "hello"})
110 parts := readFiles(headers(t, map[string][]byte{"bundle.zip": raw}))
111 if parts[0].Err == "" {
112 t.Fatal("a plain zip was accepted")
113 }
114 if !strings.Contains(parts[0].Err, "files inside it") {
115 t.Errorf("err = %q, want it to say what to do instead", parts[0].Err)
116 }
117}
118
119// An office document is a zip, so it has to be caught before the binary sniff
120// or every docx is refused as binary.
121func TestAnOfficeFileIsNotTreatedAsBinary(t *testing.T) {
122 raw := zipOf(t, map[string]string{"word/document.xml": `<w:document xmlns:w="x"><w:body><w:p><w:r><w:t>hello</w:t></w:r></w:p></w:body></w:document>`})
123 parts := readFiles(headers(t, map[string][]byte{"notes.docx": raw}))
124 if parts[0].Err != "" {
125 t.Fatalf("refused: %s", parts[0].Err)
126 }
127 if parts[0].Kind != "docx" {
128 t.Errorf("kind = %q, want docx", parts[0].Kind)
129 }
130 if !strings.Contains(parts[0].Text, "hello") {
131 t.Errorf("text = %q", parts[0].Text)
132 }
133}
134
135func TestCollapseBlankLines(t *testing.T) {
136 got := collapseBlankLines("a\n\n\n\nb\n \n\nc\n")
137 if got != "a\n\nb\n\nc" {
138 t.Errorf("got %q", got)
139 }
140}