builder_test.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package response // import "miniflux.app/v2/internal/http/response"
  4. import (
  5. "errors"
  6. "net/http"
  7. "net/http/httptest"
  8. "strings"
  9. "testing"
  10. "time"
  11. )
  12. func TestResponseHasCommonHeaders(t *testing.T) {
  13. r, err := http.NewRequest("GET", "/", nil)
  14. if err != nil {
  15. t.Fatal(err)
  16. }
  17. w := httptest.NewRecorder()
  18. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  19. New(w, r).Write()
  20. })
  21. handler.ServeHTTP(w, r)
  22. resp := w.Result()
  23. headers := map[string]string{
  24. "X-XSS-Protection": "1; mode=block",
  25. "X-Content-Type-Options": "nosniff",
  26. "X-Frame-Options": "DENY",
  27. }
  28. for header, expected := range headers {
  29. actual := resp.Header.Get(header)
  30. if actual != expected {
  31. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  32. }
  33. }
  34. }
  35. func TestBuildResponseWithCustomStatusCode(t *testing.T) {
  36. r, err := http.NewRequest("GET", "/", nil)
  37. if err != nil {
  38. t.Fatal(err)
  39. }
  40. w := httptest.NewRecorder()
  41. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  42. New(w, r).WithStatus(http.StatusNotAcceptable).Write()
  43. })
  44. handler.ServeHTTP(w, r)
  45. resp := w.Result()
  46. expectedStatusCode := http.StatusNotAcceptable
  47. if resp.StatusCode != expectedStatusCode {
  48. t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
  49. }
  50. }
  51. func TestBuildResponseWithCustomHeader(t *testing.T) {
  52. r, err := http.NewRequest("GET", "/", nil)
  53. if err != nil {
  54. t.Fatal(err)
  55. }
  56. w := httptest.NewRecorder()
  57. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  58. New(w, r).WithHeader("X-My-Header", "Value").Write()
  59. })
  60. handler.ServeHTTP(w, r)
  61. resp := w.Result()
  62. expected := "Value"
  63. actual := resp.Header.Get("X-My-Header")
  64. if actual != expected {
  65. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  66. }
  67. }
  68. func TestBuildResponseWithAttachment(t *testing.T) {
  69. r, err := http.NewRequest("GET", "/", nil)
  70. if err != nil {
  71. t.Fatal(err)
  72. }
  73. w := httptest.NewRecorder()
  74. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  75. New(w, r).WithAttachment("my_file.pdf").Write()
  76. })
  77. handler.ServeHTTP(w, r)
  78. resp := w.Result()
  79. expected := "attachment; filename=my_file.pdf"
  80. actual := resp.Header.Get("Content-Disposition")
  81. if actual != expected {
  82. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  83. }
  84. }
  85. func TestBuildResponseWithError(t *testing.T) {
  86. r, err := http.NewRequest("GET", "/", nil)
  87. if err != nil {
  88. t.Fatal(err)
  89. }
  90. w := httptest.NewRecorder()
  91. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  92. New(w, r).WithBody(errors.New("Some error")).Write()
  93. })
  94. handler.ServeHTTP(w, r)
  95. expectedBody := `Some error`
  96. actualBody := w.Body.String()
  97. if actualBody != expectedBody {
  98. t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
  99. }
  100. }
  101. func TestBuildResponseWithByteBody(t *testing.T) {
  102. r, err := http.NewRequest("GET", "/", nil)
  103. if err != nil {
  104. t.Fatal(err)
  105. }
  106. w := httptest.NewRecorder()
  107. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  108. New(w, r).WithBody([]byte("body")).Write()
  109. })
  110. handler.ServeHTTP(w, r)
  111. expectedBody := `body`
  112. actualBody := w.Body.String()
  113. if actualBody != expectedBody {
  114. t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
  115. }
  116. }
  117. func TestBuildResponseWithCachingEnabled(t *testing.T) {
  118. r, err := http.NewRequest("GET", "/", nil)
  119. if err != nil {
  120. t.Fatal(err)
  121. }
  122. w := httptest.NewRecorder()
  123. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  124. New(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
  125. b.WithBody("cached body")
  126. b.Write()
  127. })
  128. })
  129. handler.ServeHTTP(w, r)
  130. resp := w.Result()
  131. expectedStatusCode := http.StatusOK
  132. if resp.StatusCode != expectedStatusCode {
  133. t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
  134. }
  135. expectedBody := `cached body`
  136. actualBody := w.Body.String()
  137. if actualBody != expectedBody {
  138. t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
  139. }
  140. expectedHeader := "public"
  141. actualHeader := resp.Header.Get("Cache-Control")
  142. if actualHeader != expectedHeader {
  143. t.Fatalf(`Unexpected cache control header, got %q instead of %q`, actualHeader, expectedHeader)
  144. }
  145. if resp.Header.Get("Expires") == "" {
  146. t.Fatalf(`Expires header should not be empty`)
  147. }
  148. }
  149. func TestBuildResponseWithCachingAndEtag(t *testing.T) {
  150. r, err := http.NewRequest("GET", "/", nil)
  151. r.Header.Set("If-None-Match", "etag")
  152. if err != nil {
  153. t.Fatal(err)
  154. }
  155. w := httptest.NewRecorder()
  156. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  157. New(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
  158. b.WithBody("cached body")
  159. b.Write()
  160. })
  161. })
  162. handler.ServeHTTP(w, r)
  163. resp := w.Result()
  164. expectedStatusCode := http.StatusNotModified
  165. if resp.StatusCode != expectedStatusCode {
  166. t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
  167. }
  168. expectedBody := ``
  169. actualBody := w.Body.String()
  170. if actualBody != expectedBody {
  171. t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
  172. }
  173. expectedHeader := "public"
  174. actualHeader := resp.Header.Get("Cache-Control")
  175. if actualHeader != expectedHeader {
  176. t.Fatalf(`Unexpected cache control header, got %q instead of %q`, actualHeader, expectedHeader)
  177. }
  178. if resp.Header.Get("Expires") == "" {
  179. t.Fatalf(`Expires header should not be empty`)
  180. }
  181. }
  182. func TestBuildResponseWithGzipCompression(t *testing.T) {
  183. body := strings.Repeat("a", compressionThreshold+1)
  184. r, err := http.NewRequest("GET", "/", nil)
  185. r.Header.Set("Accept-Encoding", "gzip, deflate, br")
  186. if err != nil {
  187. t.Fatal(err)
  188. }
  189. w := httptest.NewRecorder()
  190. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  191. New(w, r).WithBody(body).Write()
  192. })
  193. handler.ServeHTTP(w, r)
  194. resp := w.Result()
  195. expected := "gzip"
  196. actual := resp.Header.Get("Content-Encoding")
  197. if actual != expected {
  198. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  199. }
  200. }
  201. func TestBuildResponseWithDeflateCompression(t *testing.T) {
  202. body := strings.Repeat("a", compressionThreshold+1)
  203. r, err := http.NewRequest("GET", "/", nil)
  204. r.Header.Set("Accept-Encoding", "deflate")
  205. if err != nil {
  206. t.Fatal(err)
  207. }
  208. w := httptest.NewRecorder()
  209. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  210. New(w, r).WithBody(body).Write()
  211. })
  212. handler.ServeHTTP(w, r)
  213. resp := w.Result()
  214. expected := "deflate"
  215. actual := resp.Header.Get("Content-Encoding")
  216. if actual != expected {
  217. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  218. }
  219. }
  220. func TestBuildResponseWithCompressionDisabled(t *testing.T) {
  221. body := strings.Repeat("a", compressionThreshold+1)
  222. r, err := http.NewRequest("GET", "/", nil)
  223. r.Header.Set("Accept-Encoding", "deflate")
  224. if err != nil {
  225. t.Fatal(err)
  226. }
  227. w := httptest.NewRecorder()
  228. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  229. New(w, r).WithBody(body).WithoutCompression().Write()
  230. })
  231. handler.ServeHTTP(w, r)
  232. resp := w.Result()
  233. expected := ""
  234. actual := resp.Header.Get("Content-Encoding")
  235. if actual != expected {
  236. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  237. }
  238. }
  239. func TestBuildResponseWithDeflateCompressionAndSmallPayload(t *testing.T) {
  240. body := strings.Repeat("a", compressionThreshold)
  241. r, err := http.NewRequest("GET", "/", nil)
  242. r.Header.Set("Accept-Encoding", "deflate")
  243. if err != nil {
  244. t.Fatal(err)
  245. }
  246. w := httptest.NewRecorder()
  247. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  248. New(w, r).WithBody(body).Write()
  249. })
  250. handler.ServeHTTP(w, r)
  251. resp := w.Result()
  252. expected := ""
  253. actual := resp.Header.Get("Content-Encoding")
  254. if actual != expected {
  255. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  256. }
  257. }
  258. func TestBuildResponseWithoutCompressionHeader(t *testing.T) {
  259. body := strings.Repeat("a", compressionThreshold+1)
  260. r, err := http.NewRequest("GET", "/", nil)
  261. if err != nil {
  262. t.Fatal(err)
  263. }
  264. w := httptest.NewRecorder()
  265. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  266. New(w, r).WithBody(body).Write()
  267. })
  268. handler.ServeHTTP(w, r)
  269. resp := w.Result()
  270. expected := ""
  271. actual := resp.Header.Get("Content-Encoding")
  272. if actual != expected {
  273. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  274. }
  275. }