github.com/google/cloudprober@v0.11.3/validators/http/http_test.go (about) 1 // Copyright 2018 The Cloudprober Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package http 16 17 import ( 18 "fmt" 19 "net/http" 20 "reflect" 21 "regexp" 22 "testing" 23 24 "github.com/golang/protobuf/proto" 25 "github.com/google/cloudprober/logger" 26 configpb "github.com/google/cloudprober/validators/http/proto" 27 ) 28 29 func TestParseStatusCodeConfig(t *testing.T) { 30 testStr := "302,200-299,403" 31 numRanges, err := parseStatusCodeConfig(testStr) 32 33 if err != nil { 34 t.Errorf("parseStatusCodeConfig(%s): got error: %v", testStr, err) 35 } 36 37 expectedNR := []*numRange{ 38 &numRange{ 39 lower: 302, 40 upper: 302, 41 }, 42 &numRange{ 43 lower: 200, 44 upper: 299, 45 }, 46 &numRange{ 47 lower: 403, 48 upper: 403, 49 }, 50 } 51 52 if len(numRanges) != len(expectedNR) { 53 t.Errorf("parseStatusCodeConfig(%s): len(numRanges): %d, expected: %d", testStr, len(numRanges), len(expectedNR)) 54 } 55 56 for i, nr := range numRanges { 57 if !reflect.DeepEqual(nr, expectedNR[i]) { 58 t.Errorf("parseStatusCodeConfig(%s): nr[%d]: %v, expected[%d]: %v", testStr, i, nr, i, expectedNR[i]) 59 } 60 } 61 62 // Verify that parsing invalid status code strings result in an error. 63 invalidTestStr := []string{ 64 "30a,404", 65 "301,299-200", 66 "301,200-299-400", 67 } 68 for _, s := range invalidTestStr { 69 numRanges, err := parseStatusCodeConfig(s) 70 if err == nil { 71 t.Errorf("parseStatusCodeConfig(%s): expected error but got response: %v", s, numRanges) 72 } 73 } 74 } 75 76 func TestLookupStatusCode(t *testing.T) { 77 testStr := "302,200-299,403" 78 numRanges, _ := parseStatusCodeConfig(testStr) 79 80 var found bool 81 for _, code := range []int{200, 204, 302, 403} { 82 found = lookupStatusCode(code, numRanges) 83 if !found { 84 t.Errorf("lookupStatusCode(%d, nr): %v, expected: true", code, found) 85 } 86 } 87 88 for _, code := range []int{404, 500, 502, 301} { 89 found = lookupStatusCode(code, numRanges) 90 if found { 91 t.Errorf("lookupStatusCode(%d, nr): %v, expected: false", code, found) 92 } 93 } 94 } 95 96 func TestLookupHTTPHeader(t *testing.T) { 97 var header string 98 99 headers := http.Header{"X-Success": []string{"some", "truly", "last"}} 100 101 header = "X-Failure" 102 if lookupHTTPHeader(headers, header, nil) != false { 103 t.Errorf("lookupHTTPHeader(&%T%+v, %v, %v): true, expected false", headers, headers, header, nil) 104 } 105 106 header = "X-Success" 107 if lookupHTTPHeader(headers, header, nil) != true { 108 t.Errorf("lookupHTTPHeader(&%T%+v, %v, %v): false expected: true", headers, headers, header, nil) 109 } 110 111 r := regexp.MustCompile("badl[ya]") 112 if lookupHTTPHeader(headers, header, r) != false { 113 t.Errorf("lookupHTTPHeader(&%T%+v, %v, %v): true expected: false", headers, headers, header, r) 114 } 115 116 r = regexp.MustCompile("tr[ul]ly") 117 if lookupHTTPHeader(headers, header, r) != true { 118 t.Errorf("lookupHTTPHeader(&%T%+v, %v, %v): false expected: true", headers, headers, header, r) 119 } 120 121 } 122 123 func TestValidateStatusCode(t *testing.T) { 124 testConfig := &configpb.Validator{ 125 SuccessStatusCodes: proto.String("200-299,301,302,404"), 126 FailureStatusCodes: proto.String("403,404,500-502"), 127 } 128 129 v := &Validator{} 130 err := v.Init(testConfig, &logger.Logger{}) 131 if err != nil { 132 t.Errorf("Init(%v, l): err: %v", testConfig, err) 133 } 134 135 for _, code := range []int{200, 204, 302} { 136 expected := true 137 res := &http.Response{ 138 StatusCode: code, 139 } 140 result, _ := v.Validate(res, nil) 141 if result != expected { 142 t.Errorf("v.Validate(&http.Response{StatusCode: %d}, nil): %v, expected: %v", code, result, expected) 143 } 144 } 145 146 for _, code := range []int{501, 502, 403, 404} { 147 expected := false 148 res := &http.Response{ 149 StatusCode: code, 150 } 151 result, _ := v.Validate(res, nil) 152 if result != expected { 153 t.Errorf("v.Validate(&http.Response{StatusCode: %d}, nil): %v, expected: %v", code, result, expected) 154 } 155 } 156 } 157 158 func TestValidateHeaders(t *testing.T) { 159 testConfig := &configpb.Validator{} 160 161 v := &Validator{} 162 err := v.Init(testConfig, &logger.Logger{}) 163 if err != nil { 164 t.Errorf("Init(%v, l): err: %v", testConfig, err) 165 } 166 167 respStatus := http.StatusOK 168 respHeader := http.Header{ 169 "X-Success": []string{"some", "truly", "last"}, 170 "X-Bad": []string{"some-bad"}, 171 } 172 173 for _, test := range []struct { 174 sStatus string 175 fStatus string 176 sHeader []string 177 fHeader []string 178 wantInitError bool 179 wantValid bool 180 }{ 181 { 182 sHeader: []string{"", ""}, 183 wantInitError: true, // No header name 184 }, 185 { 186 sHeader: []string{"X-Success", "[bad_regex"}, 187 wantInitError: true, // Bad regex. 188 }, 189 { 190 sHeader: []string{"X-Success", ""}, 191 wantValid: true, 192 }, 193 { 194 sHeader: []string{"X-Success", "s[om]me"}, 195 wantValid: true, 196 }, 197 { 198 sHeader: []string{"X-Success", "best"}, 199 wantValid: false, 200 }, 201 { 202 sHeader: []string{"X-Success", ""}, 203 fHeader: []string{"X-Bad", ""}, 204 wantValid: false, // Bad header is also present, so we fail. 205 }, 206 { 207 sHeader: []string{"X-Success", ""}, 208 fHeader: []string{"X-Bad", "really-bad"}, 209 wantValid: true, // Not bad enough. 210 }, 211 { 212 sHeader: []string{"X-Success", ""}, 213 fHeader: []string{"X-Bad", "some-bad"}, 214 wantValid: false, // Bad enough. 215 }, 216 { 217 sStatus: "302", 218 sHeader: []string{"X-Success", ""}, 219 wantValid: false, // Fails because of status code mismatch: 200 vs 302. 220 }, 221 } { 222 t.Run(fmt.Sprintf("%v", test), func(t *testing.T) { 223 testConfig := &configpb.Validator{ 224 SuccessStatusCodes: proto.String(test.sStatus), 225 FailureStatusCodes: proto.String(test.fStatus), 226 } 227 228 if test.sHeader != nil { 229 testConfig.SuccessHeader = &configpb.Validator_Header{ 230 Name: proto.String(test.sHeader[0]), 231 ValueRegex: proto.String(test.sHeader[1]), 232 } 233 } 234 235 if test.fHeader != nil { 236 testConfig.FailureHeader = &configpb.Validator_Header{ 237 Name: proto.String(test.fHeader[0]), 238 ValueRegex: proto.String(test.fHeader[1]), 239 } 240 } 241 242 v := &Validator{} 243 err := v.Init(testConfig, &logger.Logger{}) 244 if (err != nil) != test.wantInitError { 245 t.Errorf("Init(%v, l): got err: %v, wantError: %v", testConfig, err, test.wantInitError) 246 } 247 if err != nil { 248 return 249 } 250 251 resp := &http.Response{ 252 Header: respHeader, 253 StatusCode: respStatus, 254 } 255 ok, err := v.Validate(resp, nil) 256 257 if err != nil { 258 t.Errorf("Error running validate (resp: %v): %v", resp, err) 259 } 260 261 if ok != test.wantValid { 262 t.Errorf("validation passed: %v, wanted to pass: %v", ok, test.wantValid) 263 } 264 }) 265 } 266 }