Add result related tags and fields to http_response (#3814)

This commit is contained in:
Germán Jaber
2018-03-08 16:55:59 -06:00
committed by Daniel Nelson
parent 8005883de8
commit 81a93fcddf
3 changed files with 496 additions and 111 deletions

View File

@@ -15,6 +15,68 @@ import (
"github.com/stretchr/testify/require"
)
// Receives a list with fields that are expected to be absent
func checkAbsentFields(t *testing.T, fields []string, acc testutil.Accumulator) {
for _, field := range fields {
ok := acc.HasField("http_response", field)
require.False(t, ok)
}
}
// Receives a list with tags that are expected to be absent
func checkAbsentTags(t *testing.T, tags []string, acc testutil.Accumulator) {
for _, tag := range tags {
ok := acc.HasTag("http_response", tag)
require.False(t, ok)
}
}
// Receives a dictionary and with expected fields and their values. If a value is nil, it will only check
// that the field exists, but not its contents
func checkFields(t *testing.T, fields map[string]interface{}, acc testutil.Accumulator) {
for key, field := range fields {
switch v := field.(type) {
case int:
value, ok := acc.IntField("http_response", key)
require.True(t, ok)
require.Equal(t, field, value)
case float64:
value, ok := acc.FloatField("http_response", key)
require.True(t, ok)
require.Equal(t, field, value)
case string:
value, ok := acc.StringField("http_response", key)
require.True(t, ok)
require.Equal(t, field, value)
case nil:
ok := acc.HasField("http_response", key)
require.True(t, ok)
default:
t.Log("Unsupported type for field: ", v)
t.Fail()
}
}
}
// Receives a dictionary and with expected tags and their values. If a value is nil, it will only check
// that the tag exists, but not its contents
func checkTags(t *testing.T, tags map[string]interface{}, acc testutil.Accumulator) {
for key, tag := range tags {
switch v := tag.(type) {
case string:
ok := acc.HasTag("http_response", key)
require.True(t, ok)
require.Equal(t, tag, acc.TagValue("http_response", key))
case nil:
ok := acc.HasTag("http_response", key)
require.True(t, ok)
default:
t.Log("Unsupported type for tag: ", v)
t.Fail()
}
}
}
func setUpTestMux() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/redirect", func(w http.ResponseWriter, req *http.Request) {
@@ -56,6 +118,24 @@ func setUpTestMux() http.Handler {
return mux
}
func checkOutput(t *testing.T, acc testutil.Accumulator, presentFields map[string]interface{}, presentTags map[string]interface{}, absentFields []string, absentTags []string) {
if presentFields != nil {
checkFields(t, presentFields, acc)
}
if presentTags != nil {
checkTags(t, presentTags, acc)
}
if absentFields != nil {
checkAbsentFields(t, absentFields, acc)
}
if absentTags != nil {
checkAbsentTags(t, absentTags, acc)
}
}
func TestHeaders(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cHeader := r.Header.Get("Content-Type")
@@ -78,9 +158,20 @@ func TestHeaders(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
value, ok := acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusOK, value)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusOK,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "200",
"result": "success",
}
absentFields := []string{"response_string_match"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, nil)
}
func TestFields(t *testing.T) {
@@ -103,12 +194,20 @@ func TestFields(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
value, ok := acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusOK, value)
response_value, ok := acc.StringField("http_response", "result_type")
require.True(t, ok)
require.Equal(t, "success", response_value)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusOK,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "200",
"result": "success",
}
absentFields := []string{"response_string_match"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, nil)
}
func TestRedirects(t *testing.T) {
@@ -130,9 +229,20 @@ func TestRedirects(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
value, ok := acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusOK, value)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusOK,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "200",
"result": "success",
}
absentFields := []string{"response_string_match"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, nil)
h = &HTTPResponse{
Address: ts.URL + "/badredirect",
@@ -148,11 +258,21 @@ func TestRedirects(t *testing.T) {
err = h.Gather(&acc)
require.NoError(t, err)
value, ok = acc.IntField("http_response", "http_response_code")
require.False(t, ok)
response_value, ok := acc.StringField("http_response", "result_type")
require.True(t, ok)
require.Equal(t, "connection_failed", response_value)
expectedFields = map[string]interface{}{
"result_type": "connection_failed",
"result_code": 3,
}
expectedTags = map[string]interface{}{
"server": nil,
"method": "GET",
"result": "connection_failed",
}
absentFields = []string{"http_response_code", "response_time", "response_string_match"}
absentTags := []string{"status_code"}
checkOutput(t, acc, expectedFields, expectedTags, nil, nil)
expectedFields = map[string]interface{}{"result_type": "connection_failed"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, absentTags)
}
func TestMethod(t *testing.T) {
@@ -174,9 +294,20 @@ func TestMethod(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
value, ok := acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusOK, value)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusOK,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "POST",
"status_code": "200",
"result": "success",
}
absentFields := []string{"response_string_match"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, nil)
h = &HTTPResponse{
Address: ts.URL + "/mustbepostmethod",
@@ -192,9 +323,20 @@ func TestMethod(t *testing.T) {
err = h.Gather(&acc)
require.NoError(t, err)
value, ok = acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusMethodNotAllowed, value)
expectedFields = map[string]interface{}{
"http_response_code": http.StatusMethodNotAllowed,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags = map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "405",
"result": "success",
}
absentFields = []string{"response_string_match"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, nil)
//check that lowercase methods work correctly
h = &HTTPResponse{
@@ -211,9 +353,20 @@ func TestMethod(t *testing.T) {
err = h.Gather(&acc)
require.NoError(t, err)
value, ok = acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusMethodNotAllowed, value)
expectedFields = map[string]interface{}{
"http_response_code": http.StatusMethodNotAllowed,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags = map[string]interface{}{
"server": nil,
"method": "head",
"status_code": "405",
"result": "success",
}
absentFields = []string{"response_string_match"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, nil)
}
func TestBody(t *testing.T) {
@@ -235,9 +388,20 @@ func TestBody(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
value, ok := acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusOK, value)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusOK,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "200",
"result": "success",
}
absentFields := []string{"response_string_match"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, nil)
h = &HTTPResponse{
Address: ts.URL + "/musthaveabody",
@@ -252,9 +416,19 @@ func TestBody(t *testing.T) {
err = h.Gather(&acc)
require.NoError(t, err)
value, ok = acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusBadRequest, value)
expectedFields = map[string]interface{}{
"http_response_code": http.StatusBadRequest,
"result_type": "success",
"result_code": 0,
}
expectedTags = map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "400",
"result": "success",
}
absentFields = []string{"response_string_match"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, nil)
}
func TestStringMatch(t *testing.T) {
@@ -277,17 +451,20 @@ func TestStringMatch(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
value, ok := acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusOK, value)
value, ok = acc.IntField("http_response", "response_string_match")
require.True(t, ok)
require.Equal(t, 1, value)
response_value, ok := acc.StringField("http_response", "result_type")
require.True(t, ok)
require.Equal(t, "success", response_value)
_, ok = acc.FloatField("http_response", "response_time")
require.True(t, ok)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusOK,
"response_string_match": 1,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "200",
"result": "success",
}
checkOutput(t, acc, expectedFields, expectedTags, nil, nil)
}
func TestStringMatchJson(t *testing.T) {
@@ -310,17 +487,20 @@ func TestStringMatchJson(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
value, ok := acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusOK, value)
value, ok = acc.IntField("http_response", "response_string_match")
require.True(t, ok)
require.Equal(t, 1, value)
response_value, ok := acc.StringField("http_response", "result_type")
require.True(t, ok)
require.Equal(t, "success", response_value)
_, ok = acc.FloatField("http_response", "response_time")
require.True(t, ok)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusOK,
"response_string_match": 1,
"result_type": "success",
"result_code": 0,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "200",
"result": "success",
}
checkOutput(t, acc, expectedFields, expectedTags, nil, nil)
}
func TestStringMatchFail(t *testing.T) {
@@ -344,17 +524,20 @@ func TestStringMatchFail(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
value, ok := acc.IntField("http_response", "http_response_code")
require.True(t, ok)
require.Equal(t, http.StatusOK, value)
value, ok = acc.IntField("http_response", "response_string_match")
require.True(t, ok)
require.Equal(t, 0, value)
response_value, ok := acc.StringField("http_response", "result_type")
require.True(t, ok)
require.Equal(t, "response_string_mismatch", response_value)
_, ok = acc.FloatField("http_response", "response_time")
require.True(t, ok)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusOK,
"response_string_match": 0,
"result_type": "response_string_mismatch",
"result_code": 1,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "200",
"result": "response_string_mismatch",
}
checkOutput(t, acc, expectedFields, expectedTags, nil, nil)
}
func TestTimeout(t *testing.T) {
@@ -380,11 +563,126 @@ func TestTimeout(t *testing.T) {
err := h.Gather(&acc)
require.NoError(t, err)
_, ok := acc.IntField("http_response", "http_response_code")
require.False(t, ok)
response_value, ok := acc.StringField("http_response", "result_type")
require.True(t, ok)
require.Equal(t, "timeout", response_value)
_, ok = acc.FloatField("http_response", "response_time")
require.False(t, ok)
expectedFields := map[string]interface{}{
"result_type": "timeout",
"result_code": 4,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"result": "timeout",
}
absentFields := []string{"http_response_code", "response_time", "response_string_match"}
absentTags := []string{"status_code"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, absentTags)
}
func TestPluginErrors(t *testing.T) {
mux := setUpTestMux()
ts := httptest.NewServer(mux)
defer ts.Close()
// Bad regex test. Should return an error and return nothing
h := &HTTPResponse{
Address: ts.URL + "/good",
Body: "{ 'test': 'data'}",
Method: "GET",
ResponseStringMatch: "bad regex:[[",
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
Headers: map[string]string{
"Content-Type": "application/json",
},
FollowRedirects: true,
}
var acc testutil.Accumulator
err := h.Gather(&acc)
require.Error(t, err)
absentFields := []string{"http_response_code", "response_time", "response_string_match", "result_type", "result_code"}
absentTags := []string{"status_code", "result", "server", "method"}
checkOutput(t, acc, nil, nil, absentFields, absentTags)
// Attempt to read empty body test
h = &HTTPResponse{
Address: ts.URL + "/redirect",
Body: "",
Method: "GET",
ResponseStringMatch: ".*",
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
FollowRedirects: false,
}
acc = testutil.Accumulator{}
err = h.Gather(&acc)
require.NoError(t, err)
expectedFields := map[string]interface{}{
"http_response_code": http.StatusMovedPermanently,
"response_string_match": 0,
"result_type": "body_read_error",
"result_code": 2,
"response_time": nil,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"status_code": "301",
"result": "body_read_error",
}
checkOutput(t, acc, expectedFields, expectedTags, nil, nil)
}
func TestNetworkErrors(t *testing.T) {
// DNS error
h := &HTTPResponse{
Address: "https://nonexistent.nonexistent", // Any non-resolvable URL works here
Body: "",
Method: "GET",
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
FollowRedirects: false,
}
var acc testutil.Accumulator
err := h.Gather(&acc)
require.NoError(t, err)
expectedFields := map[string]interface{}{
"result_type": "dns_error",
"result_code": 5,
}
expectedTags := map[string]interface{}{
"server": nil,
"method": "GET",
"result": "dns_error",
}
absentFields := []string{"http_response_code", "response_time", "response_string_match"}
absentTags := []string{"status_code"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, absentTags)
// Connecton failed
h = &HTTPResponse{
Address: "https://127.127.127.127", // Any non-routable IP works here
Body: "",
Method: "GET",
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
FollowRedirects: false,
}
acc = testutil.Accumulator{}
err = h.Gather(&acc)
require.NoError(t, err)
expectedFields = map[string]interface{}{
"result_type": "connection_failed",
"result_code": 3,
}
expectedTags = map[string]interface{}{
"server": nil,
"method": "GET",
"result": "connection_failed",
}
absentFields = []string{"http_response_code", "response_time", "response_string_match"}
absentTags = []string{"status_code"}
checkOutput(t, acc, expectedFields, expectedTags, absentFields, absentTags)
}