cache_test.go
1package middleware
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "testing"
7 "time"
8)
9
10func TestCache_DefaultPublic(t *testing.T) {
11 handler := Cache(1 * time.Hour)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12 w.WriteHeader(http.StatusOK)
13 }))
14
15 req := httptest.NewRequest(http.MethodGet, "/", nil)
16 rr := httptest.NewRecorder()
17 handler.ServeHTTP(rr, req)
18
19 want := "public, max-age=3600"
20 if got := rr.Header().Get("Cache-Control"); got != want {
21 t.Errorf("Cache-Control = %q, want %q", got, want)
22 }
23}
24
25func TestCache_WithPrivate(t *testing.T) {
26 handler := Cache(5*time.Minute, WithPrivate())(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
27 w.WriteHeader(http.StatusOK)
28 }))
29
30 req := httptest.NewRequest(http.MethodGet, "/", nil)
31 rr := httptest.NewRecorder()
32 handler.ServeHTTP(rr, req)
33
34 want := "private, max-age=300"
35 if got := rr.Header().Get("Cache-Control"); got != want {
36 t.Errorf("Cache-Control = %q, want %q", got, want)
37 }
38}
39
40func TestCache_WithImmutable(t *testing.T) {
41 handler := Cache(365*24*time.Hour, WithImmutable())(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
42 w.WriteHeader(http.StatusOK)
43 }))
44
45 req := httptest.NewRequest(http.MethodGet, "/", nil)
46 rr := httptest.NewRecorder()
47 handler.ServeHTTP(rr, req)
48
49 want := "public, max-age=31536000, immutable"
50 if got := rr.Header().Get("Cache-Control"); got != want {
51 t.Errorf("Cache-Control = %q, want %q", got, want)
52 }
53}
54
55func TestCache_WithPrivateAndImmutable(t *testing.T) {
56 handler := Cache(24*time.Hour, WithPrivate(), WithImmutable())(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
57 w.WriteHeader(http.StatusOK)
58 }))
59
60 req := httptest.NewRequest(http.MethodGet, "/", nil)
61 rr := httptest.NewRecorder()
62 handler.ServeHTTP(rr, req)
63
64 want := "private, max-age=86400, immutable"
65 if got := rr.Header().Get("Cache-Control"); got != want {
66 t.Errorf("Cache-Control = %q, want %q", got, want)
67 }
68}
69
70func TestCache_WithVary(t *testing.T) {
71 handler := Cache(1*time.Hour, WithVary("Accept-Encoding", "Accept-Language"))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
72 w.WriteHeader(http.StatusOK)
73 }))
74
75 req := httptest.NewRequest(http.MethodGet, "/", nil)
76 rr := httptest.NewRecorder()
77 handler.ServeHTTP(rr, req)
78
79 vary := rr.Header()["Vary"]
80 if len(vary) != 2 {
81 t.Fatalf("expected 2 Vary values, got %d", len(vary))
82 }
83 if vary[0] != "Accept-Encoding" {
84 t.Errorf("Vary[0] = %q, want Accept-Encoding", vary[0])
85 }
86 if vary[1] != "Accept-Language" {
87 t.Errorf("Vary[1] = %q, want Accept-Language", vary[1])
88 }
89}
90
91func TestCache_MaxAgeCap(t *testing.T) {
92 handler := Cache(10 * 365 * 24 * time.Hour)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
93 w.WriteHeader(http.StatusOK)
94 }))
95
96 req := httptest.NewRequest(http.MethodGet, "/", nil)
97 rr := httptest.NewRecorder()
98 handler.ServeHTTP(rr, req)
99
100 want := "public, max-age=31536000"
101 if got := rr.Header().Get("Cache-Control"); got != want {
102 t.Errorf("Cache-Control = %q, want %q", got, want)
103 }
104}
105
106func TestCache_WithNoStore(t *testing.T) {
107 handler := Cache(0, WithNoStore())(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
108 w.WriteHeader(http.StatusOK)
109 }))
110
111 req := httptest.NewRequest(http.MethodGet, "/", nil)
112 rr := httptest.NewRecorder()
113 handler.ServeHTTP(rr, req)
114
115 want := "no-store"
116 if got := rr.Header().Get("Cache-Control"); got != want {
117 t.Errorf("Cache-Control = %q, want %q", got, want)
118 }
119}
120
121func TestCache_WithNoStoreOverridesOtherOptions(t *testing.T) {
122 handler := Cache(1*time.Hour, WithNoStore(), WithPrivate(), WithImmutable(), WithVary("Accept-Encoding"))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
123 w.WriteHeader(http.StatusOK)
124 }))
125
126 req := httptest.NewRequest(http.MethodGet, "/", nil)
127 rr := httptest.NewRecorder()
128 handler.ServeHTTP(rr, req)
129
130 want := "no-store"
131 if got := rr.Header().Get("Cache-Control"); got != want {
132 t.Errorf("Cache-Control = %q, want %q", got, want)
133 }
134 // no-store takes precedence over all other directives, including Vary
135 if got := rr.Header().Get("Vary"); got != "" {
136 t.Errorf("Vary = %q, want empty", got)
137 }
138}
139
140func TestCache_NextHandlerCalled(t *testing.T) {
141 called := false
142 handler := Cache(1 * time.Hour)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
143 called = true
144 w.WriteHeader(http.StatusOK)
145 }))
146
147 req := httptest.NewRequest(http.MethodGet, "/", nil)
148 rr := httptest.NewRecorder()
149 handler.ServeHTTP(rr, req)
150
151 if !called {
152 t.Error("next handler was not called")
153 }
154}
155
156func TestCache_HandlerCanOverride(t *testing.T) {
157 handler := Cache(1 * time.Hour)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
158 w.Header().Set("Cache-Control", "no-cache")
159 w.WriteHeader(http.StatusOK)
160 }))
161
162 req := httptest.NewRequest(http.MethodGet, "/", nil)
163 rr := httptest.NewRecorder()
164 handler.ServeHTTP(rr, req)
165
166 want := "no-cache"
167 if got := rr.Header().Get("Cache-Control"); got != want {
168 t.Errorf("Cache-Control = %q, want %q", got, want)
169 }
170}