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  }