Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

395 commit(s) on main

update readme Keenan Nemetz · 26m ago 32290114
history blame raw 3.5 KiB
1package web
2
3import (
4 "fmt"
5 "net/http"
6 "net/http/httptest"
7 "strings"
8 "testing"
9
10 "rodent.software/vole/forge/config"
11)
12
13func newTestWeb(t *testing.T) *Web {
14 t.Helper()
15 w, err := New(nil, nil, nil, nil, &config.Config{})
16 if err != nil {
17 t.Fatalf("new web: %v", err)
18 }
19 return w
20}
21
22func TestNotFoundPageIsStyled(t *testing.T) {
23 w := newTestWeb(t)
24 rw := httptest.NewRecorder()
25 w.notFound(rw, httptest.NewRequest("GET", "/example/issues/nope", nil))
26
27 if rw.Code != http.StatusNotFound {
28 t.Fatalf("status = %d, want 404", rw.Code)
29 }
30 body := rw.Body.String()
31 for _, want := range []string{"empty-page", "Not found", "Back to repositories"} {
32 if !strings.Contains(body, want) {
33 t.Fatalf("404 page does not show %q:\n%s", want, body)
34 }
35 }
36}
37
38func TestServerErrorPageIsStyledAndHidesTheDetail(t *testing.T) {
39 w := newTestWeb(t)
40 var logged string
41 w.Logger = func(format string, args ...any) {
42 logged = fmt.Sprintf(format, args...)
43 }
44
45 rw := httptest.NewRecorder()
46 w.serverError(rw, httptest.NewRequest("GET", "/example/issues", nil), "store at /srv/data failed")
47
48 if rw.Code != http.StatusInternalServerError {
49 t.Fatalf("status = %d, want 500", rw.Code)
50 }
51 if got := rw.Header().Get("Content-Type"); !strings.Contains(got, "text/html") {
52 t.Fatalf("content type = %q, want HTML", got)
53 }
54 body := rw.Body.String()
55 for _, want := range []string{"empty-page", "Server error", "Back to repositories"} {
56 if !strings.Contains(body, want) {
57 t.Fatalf("500 page does not show %q:\n%s", want, body)
58 }
59 }
60 if strings.Contains(body, "/srv/data") {
61 t.Fatalf("500 page leaked the internal detail:\n%s", body)
62 }
63 if !strings.Contains(logged, "/srv/data") || !strings.Contains(logged, "/example/issues") {
64 t.Fatalf("error detail was not logged with the request: %q", logged)
65 }
66}
67
68func TestARenderErrorIsAServerError(t *testing.T) {
69 w := newTestWeb(t)
70 w.Logger = func(string, ...any) {}
71 rw := httptest.NewRecorder()
72 // A template that does not exist fails the way a bad field name does: mid
73 // execution, after html/template has already written whatever came before it.
74 // The reader must get an error page rather than the shortened page.
75 w.render(rw, "no-such-template", nil, http.StatusOK)
76
77 if rw.Code != http.StatusInternalServerError {
78 t.Fatalf("status = %d, want 500", rw.Code)
79 }
80 if got := rw.Header().Get("Content-Type"); !strings.Contains(got, "text/plain") {
81 t.Fatalf("content type = %q, want the plain error body rather than a page", got)
82 }
83}
84
85func TestMiddlewareTurnsAPanicIntoTheServerErrorPage(t *testing.T) {
86 w := newTestWeb(t)
87 w.Logger = func(string, ...any) {}
88
89 h := w.Middleware(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
90 panic("kaboom")
91 }))
92 rw := httptest.NewRecorder()
93 h.ServeHTTP(rw, httptest.NewRequest("GET", "/", nil))
94
95 if rw.Code != http.StatusInternalServerError {
96 t.Fatalf("status = %d, want 500", rw.Code)
97 }
98 if body := rw.Body.String(); !strings.Contains(body, "Server error") || !strings.Contains(body, "empty-page") {
99 t.Fatalf("panic did not render the styled 500 page:\n%s", body)
100 }
101}
102
103func TestMiddlewareDoesNotRewriteAPartialResponse(t *testing.T) {
104 w := newTestWeb(t)
105 w.Logger = func(string, ...any) {}
106
107 h := w.Middleware(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
108 rw.WriteHeader(http.StatusOK)
109 panic("too late")
110 }))
111 defer func() {
112 if recover() == nil {
113 t.Fatal("panic after the header was written should be left to net/http")
114 }
115 }()
116 h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", "/", nil))
117}
118