package e2e import ( "fmt" "net/http" "os" "os/exec" "regexp" "strings" "testing" "github.com/barerepo/server/internal/gitread" "github.com/barerepo/server/internal/httpd" "github.com/barerepo/server/internal/repocfg" "github.com/barerepo/server/internal/thread" ) // elapsed matches the relative times a page prints, which move between two fetches and mean nothing here. var elapsed = regexp.MustCompile(`\b\d+[smhd]\b|\b\d+ (?:second|minute|hour|day|week|month|year)s? ago`) // A cache may make a page fast. It must never be the only place an answer lives. Chapter 10 item 6. func TestEveryPageRendersTheSameWithAnEmptyCache(t *testing.T) { if _, err := exec.LookPath("git"); err != nil { t.Skip("git is not installed") } in := newInstance(t) john := in.account("john") work := seed(t, in, john, "john", "johnbot") write(t, work, "retry.go", "package main\n\nfunc backoff(n int) int { return n * 2 }\n") // Seven files, so the log collapses this one and the expand link has something to open. for i := 0; i < 7; i++ { write(t, work, fmt.Sprintf("file%d.go", i), fmt.Sprintf("package main\n\nvar v%d = %d\n", i, i)) } run(t, work, "git", "add", "-A") run(t, work, "git", "commit", "-qm", "add a backoff") run(t, work, "git", "tag", "-a", "v1.0.0", "-m", "first release") run(t, work, "git", "notes", "--ref=refs/notes/releases", "add", "-m", "what changed", "v1.0.0") run(t, work, "git", "push", "-q", in.url(john, "/john/johnbot"), "master") run(t, work, "git", "push", "-q", in.url(john, "/john/johnbot"), "v1.0.0") run(t, work, "git", "push", "-q", in.url(john, "/john/johnbot"), "refs/notes/releases:refs/notes/releases") head := strings.TrimSpace(run(t, work, "git", "rev-parse", "HEAD")) paths := []string{ "/john", "/john/johnbot", "/john/johnbot?expand=" + head, "/john/johnbot/files", "/john/johnbot/files/master", "/john/johnbot/file/master/retry.go", "/john/johnbot/commit/" + head, "/john/johnbot/compare/master...master", "/john/johnbot/threads", "/john/johnbot/runs", "/john/johnbot/releases", "/john/johnbot/config", "/search?q=backoff", } warm := map[string]string{} for _, p := range paths { code, _, body := get(t, in.http.URL+p) if code != http.StatusOK { t.Fatalf("%s answered %d before the cache was emptied", p, code) } warm[p] = body } // A cache that retains nothing is the honest test, since an empty one is warm again by the second read. if err := os.RemoveAll(in.cfg.Paths.Cache); err != nil { t.Fatal(err) } warmAgain := gitread.Cache t.Cleanup(func() { gitread.Cache, repocfg.Cache, thread.Cache, httpd.Cache = warmAgain, warmAgain, warmAgain, warmAgain }) gitread.Cache = nil repocfg.Cache = nil thread.Cache = nil httpd.Cache = nil for _, p := range paths { code, _, body := get(t, in.http.URL+p) if code != http.StatusOK { t.Errorf("%s answered %d with an empty cache", p, code) continue } if normal(body) != normal(warm[p]) { t.Errorf("%s renders differently with an empty cache, so the cache holds the only copy\n"+ "cold:\n%s\nwarm:\n%s", p, body, warm[p]) } } } func normal(body string) string { return elapsed.ReplaceAllString(body, "") }