123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 | package render_test
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"strings"
"testing"
)
func TestContentsJSON(t *testing.T) {
e := newEnv(t, "proj", "")
e.seed()
e.render()
var data struct {
Repo string `json:"repo"`
SHA string `json:"sha"`
Files []struct {
P string `json:"p"`
L []string `json:"l"`
} `json:"files"`
}
if err := json.Unmarshal([]byte(e.read("search/proj/corpus.json")), &data); err != nil {
t.Fatalf("corpus: %v", err)
}
if data.Repo != "proj" || len(data.SHA) != 40 {
t.Errorf("header: %+v", data)
}
paths := map[string][]string{}
for _, f := range data.Files {
paths[f.P] = f.L
}
if _, ok := paths["bin.dat"]; ok {
t.Error("binary file included in search data")
}
if _, ok := paths["img/logo.png"]; ok {
t.Error("image included in search data")
}
if _, ok := paths["vendor/lib.min.js"]; ok {
t.Error("search_exclude glob not honoured")
}
if got := paths["src/main.c"]; len(got) != 5 || got[0] != "#include <stdio.h>" || got[4] != "}" {
t.Errorf("line splitting wrong: %q", got)
}
if gz := gunzip(t, e.h("search/proj/corpus.json.gz")); gz != e.read("search/proj/corpus.json") {
t.Error("search/proj/corpus.json.gz differs from search/proj/corpus.json")
}
}
// TestContentsVersion: the search page fetches the corpus by its hash, so
// httpd can cache it for good, and a config change that rewrites it
// without a new commit gives it a new URL.
func TestContentsVersion(t *testing.T) {
e := newEnv(t, "proj", "")
e.seed()
e.render()
url := func() string {
sum := sha256.Sum256([]byte(e.read("search/proj/corpus.json")))
return `data-data="/search/proj/corpus.json?v=` + hex.EncodeToString(sum[:])[:16] + `"`
}
before := url()
mustContain(t, "search page", e.read("search/proj/index.html"), before)
e.configure(`search_exclude: ["*.c"]` + "\n")
e.render()
after := url()
if after == before {
t.Fatal("excluding files did not change the corpus")
}
mustContain(t, "search page", e.read("search/proj/index.html"), after)
}
// TestHugeTextLines: a text file too big to serve or search still gets its
// line count in listings, streamed rather than read whole, and is not
// summarized as binary data.
func TestHugeTextLines(t *testing.T) {
e := newEnv(t, "proj", "raw_max_bytes: 64\nsearch_max_bytes: 64\n")
e.write("huge.log", strings.Repeat("a log line\n", 1000))
e.write("huge.bin", "\x00"+strings.Repeat("z\n", 1000))
e.commit("huge")
e.push(false)
e.render()
root := e.read(e.page(""))
mustContain(t, "root", root, `<span class="amount">1K lines</span>`, `<span class="amount">0 lines</span>`)
mustContain(t, "binary pill", root, `title="2 KiB of binary data across 1 file"`)
}
// TestUnknownText: a text file no lexer knows counts as code, a dotfile as
// "Dotfile" and anything else by its extension, unless it is named like
// prose (.txt, or no extension), which is still docs; a dotfile with a
// lexer of its own keeps it.
func TestUnknownText(t *testing.T) {
e := newEnv(t, "proj", "")
e.write(".clang-format", "BasedOnStyle: LLVM\nIndentWidth: 4\n")
e.write(".bashrc", "alias ll='ls -l'\n")
e.write("foo.qwerxxx", "a\nb\nc\n")
e.write("notes.txt", "some words here\n")
e.write("LICENSE", "do what you like\n")
e.commit("unknown")
e.push(false)
e.render()
root := e.read(e.page(""))
code := between(t, root, `<div class="langs code"`, "</div>")
mustContain(t, "code row", code, `>QWERXXX <b>3</b></a>`, `>Dotfile <b>2</b></a>`, `>Bash <b>1</b></a>`)
mustNotContain(t, "code row", code, "Text")
mustContain(t, "docs row", root, `>Text <b>7</b></a>`)
mustContain(t, "lines ring", root, `<circle class="code"`)
}
|