From 49fffc2d1fee7cb647d99b6e410a7d0e84cea1de Mon Sep 17 00:00:00 2001 From: Brian Brazil Date: Thu, 17 Sep 2015 15:06:21 +0100 Subject: [PATCH] Add unittests for http and tcp. Fix bug in default status codes accepted by http. Allow for how redirect errors are handled. --- http.go | 8 +++- http_test.go | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++ tcp_test.go | 49 ++++++++++++++++++++ 3 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 http_test.go create mode 100644 tcp_test.go diff --git a/http.go b/http.go index 8e14758..0d1c279 100644 --- a/http.go +++ b/http.go @@ -41,6 +41,9 @@ func probeHTTP(target string, w http.ResponseWriter, module Module) (success boo if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") { target = "http://" + target } + if config.Method == "" { + config.Method = "GET" + } request, err := http.NewRequest(config.Method, target, nil) if err != nil { @@ -48,7 +51,8 @@ func probeHTTP(target string, w http.ResponseWriter, module Module) (success boo } resp, err := client.Do(request) - if err != nil { + // Err won't be nil if redirects were turned off. See https://github.com/golang/go/issues/3795 + if err != nil && resp == nil { log.Warnf("Error for HTTP request to %s: %s", target, err) } else { defer resp.Body.Close() @@ -59,7 +63,7 @@ func probeHTTP(target string, w http.ResponseWriter, module Module) (success boo break } } - } else if 200 >= resp.StatusCode && resp.StatusCode < 300 { + } else if 200 <= resp.StatusCode && resp.StatusCode < 300 { success = true } } diff --git a/http_test.go b/http_test.go new file mode 100644 index 0000000..0302e01 --- /dev/null +++ b/http_test.go @@ -0,0 +1,127 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" +) + +func TestHTTPStatusCodes(t *testing.T) { + tests := []struct { + StatusCode int + ValidStatusCodes []int + ShouldSucceed bool + }{ + {200, []int{}, true}, + {201, []int{}, true}, + {299, []int{}, true}, + {300, []int{}, false}, + {404, []int{}, false}, + {404, []int{200, 404}, true}, + {200, []int{200, 404}, true}, + {201, []int{200, 404}, false}, + {404, []int{404}, true}, + {200, []int{404}, false}, + } + + for i, test := range tests { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(test.StatusCode) + })) + defer ts.Close() + recorder := httptest.NewRecorder() + result := probeHTTP(ts.URL, recorder, + Module{Timeout: time.Second, HTTP: HTTPProbe{ValidStatusCodes: test.ValidStatusCodes}}) + body := recorder.Body.String() + if result != test.ShouldSucceed { + t.Fatalf("Test %d had unexpected result: %s", i, body) + } + } +} + +func TestRedirectFollowed(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/" { + http.Redirect(w, r, "/noredirect", http.StatusFound) + } + })) + defer ts.Close() + + // Follow redirect, should succeed with 200. + recorder := httptest.NewRecorder() + result := probeHTTP(ts.URL, recorder, Module{Timeout: time.Second, HTTP: HTTPProbe{}}) + body := recorder.Body.String() + if !result { + t.Fatalf("Redirect test failed unexpectedly, got %s", body) + } + if !strings.Contains(body, "probe_http_redirects 1\n") { + t.Fatalf("Expected one redirect, got %s", body) + } + +} + +func TestRedirectNotFollowed(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "/noredirect", http.StatusFound) + })) + defer ts.Close() + + // Follow redirect, should succeed with 200. + recorder := httptest.NewRecorder() + result := probeHTTP(ts.URL, recorder, + Module{Timeout: time.Second, HTTP: HTTPProbe{NoFollowRedirects: true, ValidStatusCodes: []int{302}}}) + body := recorder.Body.String() + if !result { + t.Fatalf("Redirect test failed unexpectedly, got %s", body) + } + +} + +func TestPost(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(http.StatusBadRequest) + } + })) + defer ts.Close() + + recorder := httptest.NewRecorder() + result := probeHTTP(ts.URL, recorder, + Module{Timeout: time.Second, HTTP: HTTPProbe{Method: "POST"}}) + body := recorder.Body.String() + if !result { + t.Fatalf("Post test failed unexpectedly, got %s", body) + } +} + +func TestFailIfNotSSL(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + })) + defer ts.Close() + + recorder := httptest.NewRecorder() + result := probeHTTP(ts.URL, recorder, + Module{Timeout: time.Second, HTTP: HTTPProbe{FailIfNotSSL: true}}) + body := recorder.Body.String() + if result { + t.Fatalf("Fail if not SSL test suceeded unexpectedly, got %s", body) + } + if !strings.Contains(body, "probe_http_ssl 0\n") { + t.Fatalf("Expected HTTP without SSL, got %s", body) + } +} diff --git a/tcp_test.go b/tcp_test.go new file mode 100644 index 0000000..9d4ddfd --- /dev/null +++ b/tcp_test.go @@ -0,0 +1,49 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net" + "testing" + "time" +) + +func TestTCPConnection(t *testing.T) { + ln, err := net.Listen("tcp", "localhost:") + if err != nil { + t.Fatalf("Error listening on socket: %s", err) + } + defer ln.Close() + + ch := make(chan (struct{})) + go func() { + conn, err := ln.Accept() + if err != nil { + t.Fatalf("Error accepting on socket: %s", err) + } + conn.Close() + ch <- struct{}{} + }() + if !probeTCP(ln.Addr().String(), nil, Module{Timeout: time.Second}) { + t.Fatalf("TCP module failed, expected success.") + } + <-ch +} + +func TestTCPConnectionFails(t *testing.T) { + // Invalid port number. + if probeTCP(":0", nil, Module{Timeout: time.Second}) { + t.Fatalf("TCP module suceeded, expected failure.") + } +} -- 2.25.1