builder_test.go 8.6 KB

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