http_handler_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright (c) 2016 Uber Technologies, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package zap_test
  21. import (
  22. "encoding/json"
  23. "errors"
  24. "net/http"
  25. "net/http/httptest"
  26. "strings"
  27. "testing"
  28. "go.uber.org/zap"
  29. "go.uber.org/zap/zapcore"
  30. "github.com/stretchr/testify/assert"
  31. "github.com/stretchr/testify/require"
  32. )
  33. func TestAtomicLevelServeHTTP(t *testing.T) {
  34. tests := []struct {
  35. desc string
  36. method string
  37. query string
  38. contentType string
  39. body string
  40. expectedCode int
  41. expectedLevel zapcore.Level
  42. }{
  43. {
  44. desc: "GET",
  45. method: http.MethodGet,
  46. expectedCode: http.StatusOK,
  47. expectedLevel: zap.InfoLevel,
  48. },
  49. {
  50. desc: "PUT JSON",
  51. method: http.MethodPut,
  52. expectedCode: http.StatusOK,
  53. expectedLevel: zap.WarnLevel,
  54. body: `{"level":"warn"}`,
  55. },
  56. {
  57. desc: "PUT URL encoded",
  58. method: http.MethodPut,
  59. expectedCode: http.StatusOK,
  60. expectedLevel: zap.WarnLevel,
  61. contentType: "application/x-www-form-urlencoded",
  62. body: "level=warn",
  63. },
  64. {
  65. desc: "PUT query parameters",
  66. method: http.MethodPut,
  67. query: "?level=warn",
  68. expectedCode: http.StatusOK,
  69. expectedLevel: zap.WarnLevel,
  70. contentType: "application/x-www-form-urlencoded",
  71. },
  72. {
  73. desc: "body takes precedence over query",
  74. method: http.MethodPut,
  75. query: "?level=info",
  76. expectedCode: http.StatusOK,
  77. expectedLevel: zap.WarnLevel,
  78. contentType: "application/x-www-form-urlencoded",
  79. body: "level=warn",
  80. },
  81. {
  82. desc: "JSON ignores query",
  83. method: http.MethodPut,
  84. query: "?level=info",
  85. expectedCode: http.StatusOK,
  86. expectedLevel: zap.WarnLevel,
  87. body: `{"level":"warn"}`,
  88. },
  89. {
  90. desc: "PUT JSON unrecognized",
  91. method: http.MethodPut,
  92. expectedCode: http.StatusBadRequest,
  93. body: `{"level":"unrecognized"}`,
  94. },
  95. {
  96. desc: "PUT URL encoded unrecognized",
  97. method: http.MethodPut,
  98. expectedCode: http.StatusBadRequest,
  99. contentType: "application/x-www-form-urlencoded",
  100. body: "level=unrecognized",
  101. },
  102. {
  103. desc: "PUT JSON malformed",
  104. method: http.MethodPut,
  105. expectedCode: http.StatusBadRequest,
  106. body: `{"level":"warn`,
  107. },
  108. {
  109. desc: "PUT URL encoded malformed",
  110. method: http.MethodPut,
  111. query: "?level=%",
  112. expectedCode: http.StatusBadRequest,
  113. contentType: "application/x-www-form-urlencoded",
  114. },
  115. {
  116. desc: "PUT Query parameters malformed",
  117. method: http.MethodPut,
  118. expectedCode: http.StatusBadRequest,
  119. contentType: "application/x-www-form-urlencoded",
  120. body: "level=%",
  121. },
  122. {
  123. desc: "PUT JSON unspecified",
  124. method: http.MethodPut,
  125. expectedCode: http.StatusBadRequest,
  126. body: `{}`,
  127. },
  128. {
  129. desc: "PUT URL encoded unspecified",
  130. method: http.MethodPut,
  131. expectedCode: http.StatusBadRequest,
  132. contentType: "application/x-www-form-urlencoded",
  133. body: "",
  134. },
  135. {
  136. desc: "POST JSON",
  137. method: http.MethodPost,
  138. expectedCode: http.StatusMethodNotAllowed,
  139. body: `{"level":"warn"}`,
  140. },
  141. {
  142. desc: "POST URL",
  143. method: http.MethodPost,
  144. expectedCode: http.StatusMethodNotAllowed,
  145. contentType: "application/x-www-form-urlencoded",
  146. body: "level=warn",
  147. },
  148. }
  149. for _, tt := range tests {
  150. t.Run(tt.desc, func(t *testing.T) {
  151. lvl := zap.NewAtomicLevel()
  152. lvl.SetLevel(zapcore.InfoLevel)
  153. server := httptest.NewServer(lvl)
  154. defer server.Close()
  155. req, err := http.NewRequest(tt.method, server.URL+tt.query, strings.NewReader(tt.body))
  156. require.NoError(t, err, "Error constructing %s request.", req.Method)
  157. if tt.contentType != "" {
  158. req.Header.Set("Content-Type", tt.contentType)
  159. }
  160. res, err := http.DefaultClient.Do(req)
  161. require.NoError(t, err, "Error making %s request.", req.Method)
  162. defer func() {
  163. assert.NoError(t, res.Body.Close(), "Error closing response body.")
  164. }()
  165. require.Equal(t, tt.expectedCode, res.StatusCode, "Unexpected status code.")
  166. if tt.expectedCode != http.StatusOK {
  167. // Don't need to test exact error message, but one should be present.
  168. var pld struct {
  169. Error string `json:"error"`
  170. }
  171. require.NoError(t, json.NewDecoder(res.Body).Decode(&pld), "Decoding response body")
  172. assert.NotEmpty(t, pld.Error, "Expected an error message")
  173. return
  174. }
  175. var pld struct {
  176. Level zapcore.Level `json:"level"`
  177. }
  178. require.NoError(t, json.NewDecoder(res.Body).Decode(&pld), "Decoding response body")
  179. assert.Equal(t, tt.expectedLevel, pld.Level, "Unexpected logging level returned")
  180. })
  181. }
  182. }
  183. func TestAtomicLevelServeHTTPBrokenWriter(t *testing.T) {
  184. t.Parallel()
  185. lvl := zap.NewAtomicLevel()
  186. request, err := http.NewRequest(http.MethodGet, "http://localhost:1234/log/level", nil)
  187. require.NoError(t, err, "Error constructing request.")
  188. recorder := httptest.NewRecorder()
  189. lvl.ServeHTTP(&brokenHTTPResponseWriter{
  190. ResponseWriter: recorder,
  191. }, request)
  192. assert.Equal(t, http.StatusInternalServerError, recorder.Code, "Unexpected status code.")
  193. }
  194. type brokenHTTPResponseWriter struct {
  195. http.ResponseWriter
  196. }
  197. func (w *brokenHTTPResponseWriter) Write([]byte) (int, error) {
  198. return 0, errors.New("great sadness")
  199. }