carlos / movement Public

Clone
git clone https://amadan.net/carlos/movement

Plain git — no account needed to clone.

Download

Download this file

1package movementtest
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "regexp"
7 "strings"
8 "testing"
9
10 movement "amadan.net/carlos/movement/internal/movement"
11)
12
13// hashedStylesheet is the fingerprinted URL shape the index page
14// links: /static/tokens.<16 hex>.css.
15var hashedStylesheet = regexp.MustCompile(`/static/tokens\.[0-9a-f]{16}\.css`)
16
17func TestIndexRenders(t *testing.T) {
18 rec := get(t, newApp(t), "/")
19 if rec.Code != http.StatusOK {
20 t.Fatalf("GET /: status %d, want 200", rec.Code)
21 }
22 // The heading comes from the copy table, not from the template, so this
23 // asserts the wiring rather than the words. When copy review replaces
24 // the placeholder, this test does not need to change — which is the
25 // point of holding copy separately.
26 if !strings.Contains(rec.Body.String(), movement.T("index.headline")) {
27 t.Errorf("GET /: page is missing its heading:\n%s", rec.Body.String())
28 }
29}
30
31func TestIndexLinksFingerprintedStylesheet(t *testing.T) {
32 h := newApp(t)
33 href := hashedStylesheet.FindString(get(t, h, "/").Body.String())
34 if href == "" {
35 t.Fatal("index page links no fingerprinted stylesheet")
36 }
37 rec := get(t, h, href)
38 if rec.Code != http.StatusOK {
39 t.Fatalf("GET %s: status %d, want 200", href, rec.Code)
40 }
41 if got := rec.Header().Get("Cache-Control"); got != "public, max-age=31536000, immutable" {
42 t.Errorf("GET %s: Cache-Control = %q, want immutable year", href, got)
43 }
44}
45
46// The bare name keeps working, just never long-cached — so a changed
47// file always shows on an ordinary reload.
48func TestBareAssetNameStaysFresh(t *testing.T) {
49 rec := get(t, newApp(t), "/static/tokens.css")
50 if rec.Code != http.StatusOK {
51 t.Fatalf("GET /static/tokens.css: status %d, want 200", rec.Code)
52 }
53 if got := rec.Header().Get("Cache-Control"); got != "no-cache" {
54 t.Errorf("Cache-Control = %q, want no-cache", got)
55 }
56}
57
58// ErrorPage is what main.go points Options.ErrorPage at (and the seam
59// to wire into Ctx.ErrorPage as handlers grow); called directly, it
60// pins that a panic's page is the framework's error-page partial, not
61// a stray net/http default.
62func TestErrorPageRendersFrameworkPartial(t *testing.T) {
63 rec := httptest.NewRecorder()
64 movement.ErrorPage(rec, httptest.NewRequest(http.MethodGet, "/", nil), http.StatusInternalServerError, "abc123")
65 if rec.Code != http.StatusInternalServerError {
66 t.Fatalf("ErrorPage: status %d, want %d", rec.Code, http.StatusInternalServerError)
67 }
68 body := rec.Body.String()
69 if !strings.Contains(body, "Something went wrong on our side") {
70 t.Errorf("ErrorPage body missing the 500 title:\n%s", body)
71 }
72 if !strings.Contains(body, "abc123") {
73 t.Errorf("ErrorPage body missing the ref:\n%s", body)
74 }
75}
76