repos
/ orchard main

orchard

mirror

Every 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

4.1 KB · 141 lines · Go Raw History
  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}