github.com/Laisky/zap@v1.27.0/http_handler_test.go (about) 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 21 package zap_test 22 23 import ( 24 "encoding/json" 25 "errors" 26 "net/http" 27 "net/http/httptest" 28 "strings" 29 "testing" 30 31 "github.com/Laisky/zap" 32 "github.com/Laisky/zap/zapcore" 33 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 ) 37 38 func TestAtomicLevelServeHTTP(t *testing.T) { 39 tests := []struct { 40 desc string 41 method string 42 query string 43 contentType string 44 body string 45 expectedCode int 46 expectedLevel zapcore.Level 47 }{ 48 { 49 desc: "GET", 50 method: http.MethodGet, 51 expectedCode: http.StatusOK, 52 expectedLevel: zap.InfoLevel, 53 }, 54 { 55 desc: "PUT JSON", 56 method: http.MethodPut, 57 expectedCode: http.StatusOK, 58 expectedLevel: zap.WarnLevel, 59 body: `{"level":"warn"}`, 60 }, 61 { 62 desc: "PUT URL encoded", 63 method: http.MethodPut, 64 expectedCode: http.StatusOK, 65 expectedLevel: zap.WarnLevel, 66 contentType: "application/x-www-form-urlencoded", 67 body: "level=warn", 68 }, 69 { 70 desc: "PUT query parameters", 71 method: http.MethodPut, 72 query: "?level=warn", 73 expectedCode: http.StatusOK, 74 expectedLevel: zap.WarnLevel, 75 contentType: "application/x-www-form-urlencoded", 76 }, 77 { 78 desc: "body takes precedence over query", 79 method: http.MethodPut, 80 query: "?level=info", 81 expectedCode: http.StatusOK, 82 expectedLevel: zap.WarnLevel, 83 contentType: "application/x-www-form-urlencoded", 84 body: "level=warn", 85 }, 86 { 87 desc: "JSON ignores query", 88 method: http.MethodPut, 89 query: "?level=info", 90 expectedCode: http.StatusOK, 91 expectedLevel: zap.WarnLevel, 92 body: `{"level":"warn"}`, 93 }, 94 { 95 desc: "PUT JSON unrecognized", 96 method: http.MethodPut, 97 expectedCode: http.StatusBadRequest, 98 body: `{"level":"unrecognized"}`, 99 }, 100 { 101 desc: "PUT URL encoded unrecognized", 102 method: http.MethodPut, 103 expectedCode: http.StatusBadRequest, 104 contentType: "application/x-www-form-urlencoded", 105 body: "level=unrecognized", 106 }, 107 { 108 desc: "PUT JSON malformed", 109 method: http.MethodPut, 110 expectedCode: http.StatusBadRequest, 111 body: `{"level":"warn`, 112 }, 113 { 114 desc: "PUT URL encoded malformed", 115 method: http.MethodPut, 116 query: "?level=%", 117 expectedCode: http.StatusBadRequest, 118 contentType: "application/x-www-form-urlencoded", 119 }, 120 { 121 desc: "PUT Query parameters malformed", 122 method: http.MethodPut, 123 expectedCode: http.StatusBadRequest, 124 contentType: "application/x-www-form-urlencoded", 125 body: "level=%", 126 }, 127 { 128 desc: "PUT JSON unspecified", 129 method: http.MethodPut, 130 expectedCode: http.StatusBadRequest, 131 body: `{}`, 132 }, 133 { 134 desc: "PUT URL encoded unspecified", 135 method: http.MethodPut, 136 expectedCode: http.StatusBadRequest, 137 contentType: "application/x-www-form-urlencoded", 138 body: "", 139 }, 140 { 141 desc: "POST JSON", 142 method: http.MethodPost, 143 expectedCode: http.StatusMethodNotAllowed, 144 body: `{"level":"warn"}`, 145 }, 146 { 147 desc: "POST URL", 148 method: http.MethodPost, 149 expectedCode: http.StatusMethodNotAllowed, 150 contentType: "application/x-www-form-urlencoded", 151 body: "level=warn", 152 }, 153 } 154 155 for _, tt := range tests { 156 t.Run(tt.desc, func(t *testing.T) { 157 lvl := zap.NewAtomicLevel() 158 lvl.SetLevel(zapcore.InfoLevel) 159 160 server := httptest.NewServer(lvl) 161 defer server.Close() 162 163 req, err := http.NewRequest(tt.method, server.URL+tt.query, strings.NewReader(tt.body)) 164 require.NoError(t, err, "Error constructing %s request.", req.Method) 165 if tt.contentType != "" { 166 req.Header.Set("Content-Type", tt.contentType) 167 } 168 169 res, err := http.DefaultClient.Do(req) 170 require.NoError(t, err, "Error making %s request.", req.Method) 171 defer func() { 172 assert.NoError(t, res.Body.Close(), "Error closing response body.") 173 }() 174 175 require.Equal(t, tt.expectedCode, res.StatusCode, "Unexpected status code.") 176 if tt.expectedCode != http.StatusOK { 177 // Don't need to test exact error message, but one should be present. 178 var pld struct { 179 Error string `json:"error"` 180 } 181 require.NoError(t, json.NewDecoder(res.Body).Decode(&pld), "Decoding response body") 182 assert.NotEmpty(t, pld.Error, "Expected an error message") 183 return 184 } 185 186 var pld struct { 187 Level zapcore.Level `json:"level"` 188 } 189 require.NoError(t, json.NewDecoder(res.Body).Decode(&pld), "Decoding response body") 190 assert.Equal(t, tt.expectedLevel, pld.Level, "Unexpected logging level returned") 191 }) 192 } 193 } 194 195 func TestAtomicLevelServeHTTPBrokenWriter(t *testing.T) { 196 t.Parallel() 197 198 lvl := zap.NewAtomicLevel() 199 200 request, err := http.NewRequest(http.MethodGet, "http://localhost:1234/log/level", nil) 201 require.NoError(t, err, "Error constructing request.") 202 203 recorder := httptest.NewRecorder() 204 lvl.ServeHTTP(&brokenHTTPResponseWriter{ 205 ResponseWriter: recorder, 206 }, request) 207 208 assert.Equal(t, http.StatusInternalServerError, recorder.Code, "Unexpected status code.") 209 } 210 211 type brokenHTTPResponseWriter struct { 212 http.ResponseWriter 213 } 214 215 func (w *brokenHTTPResponseWriter) Write([]byte) (int, error) { 216 return 0, errors.New("great sadness") 217 }