From 61f71696e676504da1b05df0fa526f73a7d5cc97 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Mon, 9 Jul 2018 14:58:04 -0500 Subject: [PATCH 01/14] Breakup HB HTTP Tasks into separate test instances --- heartbeat/monitors/active/http/task_test.go | 56 +++++++++++++-------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/heartbeat/monitors/active/http/task_test.go b/heartbeat/monitors/active/http/task_test.go index 1b5f3756b44..4333375a7fc 100644 --- a/heartbeat/monitors/active/http/task_test.go +++ b/heartbeat/monitors/active/http/task_test.go @@ -27,6 +27,7 @@ import ( func TestSplitHostnamePort(t *testing.T) { var urlTests = []struct { + name string scheme string host string expectedHost string @@ -34,6 +35,7 @@ func TestSplitHostnamePort(t *testing.T) { expectedError error }{ { + "plain", "http", "foo", "foo", @@ -41,6 +43,7 @@ func TestSplitHostnamePort(t *testing.T) { nil, }, { + "dotted domain", "http", "www.foo.com", "www.foo.com", @@ -48,6 +51,7 @@ func TestSplitHostnamePort(t *testing.T) { nil, }, { + "dotted domain, custom port", "http", "www.foo.com:8080", "www.foo.com", @@ -55,6 +59,7 @@ func TestSplitHostnamePort(t *testing.T) { nil, }, { + "https plain", "https", "foo", "foo", @@ -62,6 +67,7 @@ func TestSplitHostnamePort(t *testing.T) { nil, }, { + "custom port", "http", "foo:81", "foo", @@ -69,6 +75,7 @@ func TestSplitHostnamePort(t *testing.T) { nil, }, { + "https custom port", "https", "foo:444", "foo", @@ -76,6 +83,7 @@ func TestSplitHostnamePort(t *testing.T) { nil, }, { + "bad scheme", "httpz", "foo", "foo", @@ -84,27 +92,33 @@ func TestSplitHostnamePort(t *testing.T) { }, } for _, test := range urlTests { - url := &url.URL{ - Scheme: test.scheme, - Host: test.host, - } - request := &http.Request{ - URL: url, - } - host, port, err := splitHostnamePort(request) - if err != nil { - if test.expectedError == nil { - t.Error(err) - } else if reflect.TypeOf(err) != reflect.TypeOf(test.expectedError) { - t.Errorf("Expected %T but got %T", err, test.expectedError) + test := test + + t.Run(test.name, func(t2 *testing.T) { + url := &url.URL{ + Scheme: test.scheme, + Host: test.host, + } + request := &http.Request{ + URL: url, } - continue - } - if host != test.expectedHost { - t.Errorf("Unexpected host for %#v: expected %q, got %q", request, test.expectedHost, host) - } - if port != test.expectedPort { - t.Errorf("Unexpected port for %#v: expected %q, got %q", request, test.expectedPort, port) - } + host, port, err := splitHostnamePort(request) + + if err != nil { + if test.expectedError == nil { + t.Error(err) + } else if reflect.TypeOf(err) != reflect.TypeOf(test.expectedError) { + t.Errorf("Expected %T but got %T", err, test.expectedError) + } + } else { + if host != test.expectedHost { + t.Errorf("Unexpected host for %#v: expected %q, got %q", request, test.expectedHost, host) + } + if port != test.expectedPort { + t.Errorf("Unexpected port for %#v: expected %q, got %q", request, test.expectedPort, port) + } + } + + }) } } From 8bdca17ca34e723021e4a510b7f5c2b152841cda Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Mon, 9 Jul 2018 15:15:37 -0500 Subject: [PATCH 02/14] Test hb http redirect check function --- heartbeat/monitors/active/http/task_test.go | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/heartbeat/monitors/active/http/task_test.go b/heartbeat/monitors/active/http/task_test.go index 4333375a7fc..a66aedc4829 100644 --- a/heartbeat/monitors/active/http/task_test.go +++ b/heartbeat/monitors/active/http/task_test.go @@ -23,6 +23,8 @@ import ( "net/url" "reflect" "testing" + + "github.com/stretchr/testify/assert" ) func TestSplitHostnamePort(t *testing.T) { @@ -122,3 +124,33 @@ func TestSplitHostnamePort(t *testing.T) { }) } } + +func makeTestHTTPRequest(t *testing.T) *http.Request { + req, err := http.NewRequest("GET", "http://example.net", nil) + assert.Nil(t, err) + return req +} + +func TestZeroMaxRedirectShouldError(t *testing.T) { + checker := makeCheckRedirect(0) + req := makeTestHTTPRequest(t) + + res := checker(req, nil) + assert.Equal(t, http.ErrUseLastResponse, res) +} + +func TestNonZeroRedirect(t *testing.T) { + limit := 5 + checker := makeCheckRedirect(limit) + + var via []*http.Request + // Test requests within the limit + for i := 0; i < limit; i++ { + req := makeTestHTTPRequest(t) + assert.Nil(t, checker(req, via)) + via = append(via, req) + } + + // We are now at the limit, this request should fail + assert.Equal(t, http.ErrUseLastResponse, checker(makeTestHTTPRequest(t), via)) +} From e89aa4167859e73916fc2f505cec2bf76c376513 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Mon, 9 Jul 2018 17:12:29 -0500 Subject: [PATCH 03/14] Break apart execPing in heartbeat/task.go --- heartbeat/monitors/active/http/task.go | 43 +++++++++++++++++++------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/heartbeat/monitors/active/http/task.go b/heartbeat/monitors/active/http/task.go index 86f4716be52..434492616c5 100644 --- a/heartbeat/monitors/active/http/task.go +++ b/heartbeat/monitors/active/http/task.go @@ -218,29 +218,53 @@ func execPing( body []byte, timeout time.Duration, validator func(*http.Response) error, -) (time.Time, time.Time, common.MapStr, reason.Reason) { +) (start time.Time, end time.Time, event common.MapStr, errReason reason.Reason) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() - req = req.WithContext(ctx) + req = attachRequestBody(&ctx, req, body) + + start, end, resp, errReason := execRequest(client, req, validator) + if errReason != nil { + return start, end, nil, errReason + } + + event = makeEvent(end.Sub(start), resp) + + return start, end, event, nil +} + +func attachRequestBody(ctx *context.Context, req *http.Request, body []byte) *http.Request { + req = req.WithContext(*ctx) if len(body) > 0 { req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) req.ContentLength = int64(len(body)) } - start := time.Now() + return req +} + +func execRequest(client *http.Client, req *http.Request, validator func(*http.Response) error) (start time.Time, end time.Time, resp *http.Response, errReason reason.Reason) { + start = time.Now() resp, err := client.Do(req) - end := time.Now() + defer resp.Body.Close() + end = time.Now() + if err != nil { return start, end, nil, reason.IOFailed(err) } - defer resp.Body.Close() err = validator(resp) end = time.Now() + if err != nil { + return start, end, resp, reason.ValidateFailed(err) + } + + return start, end, resp, nil +} - rtt := end.Sub(start) - event := common.MapStr{"http": common.MapStr{ +func makeEvent(rtt time.Duration, resp *http.Response) common.MapStr { + return common.MapStr{"http": common.MapStr{ "response": common.MapStr{ "status_code": resp.StatusCode, }, @@ -248,11 +272,6 @@ func execPing( "total": look.RTT(rtt), }, }} - - if err != nil { - return start, end, event, reason.ValidateFailed(err) - } - return start, end, event, nil } func splitHostnamePort(requ *http.Request) (string, uint16, error) { From b46b409907bb490fe15bb59bfe8deb23842728cb Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Mon, 9 Jul 2018 17:13:50 -0500 Subject: [PATCH 04/14] Break apart execPing in heartbeat/task.go --- heartbeat/monitors/active/http/task.go | 1 + heartbeat/monitors/active/http/task_test.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/heartbeat/monitors/active/http/task.go b/heartbeat/monitors/active/http/task.go index 434492616c5..61f2fd5e5dc 100644 --- a/heartbeat/monitors/active/http/task.go +++ b/heartbeat/monitors/active/http/task.go @@ -223,6 +223,7 @@ func execPing( defer cancel() req = attachRequestBody(&ctx, req, body) + defer req.Body.Close() start, end, resp, errReason := execRequest(client, req, validator) if errReason != nil { diff --git a/heartbeat/monitors/active/http/task_test.go b/heartbeat/monitors/active/http/task_test.go index a66aedc4829..57fb23fdb14 100644 --- a/heartbeat/monitors/active/http/task_test.go +++ b/heartbeat/monitors/active/http/task_test.go @@ -27,6 +27,10 @@ import ( "github.com/stretchr/testify/assert" ) +func TestPingExecution(t *testing.T) { + +} + func TestSplitHostnamePort(t *testing.T) { var urlTests = []struct { name string From c8092c1e1415b38a6e6950f52b5018aafa294e5b Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Mon, 9 Jul 2018 19:16:31 -0500 Subject: [PATCH 05/14] Add initial tests for http ping execution --- heartbeat/monitors/active/http/task.go | 14 ++- heartbeat/monitors/active/http/task_test.go | 103 +++++++++++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/heartbeat/monitors/active/http/task.go b/heartbeat/monitors/active/http/task.go index 61f2fd5e5dc..15e635d320e 100644 --- a/heartbeat/monitors/active/http/task.go +++ b/heartbeat/monitors/active/http/task.go @@ -31,6 +31,8 @@ import ( "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/outputs/transport" + "io" + "github.com/elastic/beats/heartbeat/look" "github.com/elastic/beats/heartbeat/monitors" "github.com/elastic/beats/heartbeat/monitors/active/dialchain" @@ -223,9 +225,12 @@ func execPing( defer cancel() req = attachRequestBody(&ctx, req, body) - defer req.Body.Close() + defer closeIfPresent(&req.Body) start, end, resp, errReason := execRequest(client, req, validator) + if resp != nil { // If above errors, the response will be nil + defer closeIfPresent(&resp.Body) + } if errReason != nil { return start, end, nil, errReason } @@ -235,6 +240,12 @@ func execPing( return start, end, event, nil } +func closeIfPresent(closer *io.ReadCloser) { + if *closer != nil { + (*closer).Close() + } +} + func attachRequestBody(ctx *context.Context, req *http.Request, body []byte) *http.Request { req = req.WithContext(*ctx) if len(body) > 0 { @@ -248,7 +259,6 @@ func attachRequestBody(ctx *context.Context, req *http.Request, body []byte) *ht func execRequest(client *http.Client, req *http.Request, validator func(*http.Response) error) (start time.Time, end time.Time, resp *http.Response, errReason reason.Reason) { start = time.Now() resp, err := client.Do(req) - defer resp.Body.Close() end = time.Now() if err != nil { diff --git a/heartbeat/monitors/active/http/task_test.go b/heartbeat/monitors/active/http/task_test.go index 57fb23fdb14..1646d995f49 100644 --- a/heartbeat/monitors/active/http/task_test.go +++ b/heartbeat/monitors/active/http/task_test.go @@ -24,11 +24,112 @@ import ( "reflect" "testing" + "net/http/httptest" + + "time" + + "io/ioutil" + + "io" + + "fmt" + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/heartbeat/reason" + "github.com/elastic/beats/libbeat/common" ) -func TestPingExecution(t *testing.T) { +var helloWorldBody = "hello, world!" + +var helloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + io.WriteString(w, helloWorldBody) +}) + +var badGatewayBody = "Bad Gateway" + +var badGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadGateway) + io.WriteString(w, badGatewayBody) +}) + +func testPingResponse(t *testing.T, handlerFunc http.HandlerFunc, expectedStatus int, expectedBody string) (start time.Time, end time.Time, event common.MapStr, reason reason.Reason) { + server := httptest.NewServer(handlerFunc) + defer server.Close() + + client := http.DefaultClient + req, err := http.NewRequest("GET", server.URL, nil) + assert.Nil(t, err) + + var validatorResp *http.Response = nil + var validatorBodyBytes []byte + validator := func(resp *http.Response) error { + validatorResp = resp + validatorBodyBytes, err = ioutil.ReadAll(resp.Body) + assert.Nil(t, err) + return nil + } + + start, end, event, reason = execPing(client, req, nil, time.Second, validator) + + assert.Equal(t, expectedStatus, validatorResp.StatusCode) + assert.Nil(t, err) + assert.Equal(t, expectedBody, string(validatorBodyBytes)) + + assert.Nil(t, reason) + assert.True(t, start.Before(end) || start.Equal(end)) + // More robust tests of the event can go at a higher level + assert.NotNil(t, event) + + return start, end, event, reason +} + +func TestGoodResponseCode(t *testing.T) { + testPingResponse(t, helloWorldHandler, http.StatusOK, helloWorldBody) +} + +// Non 2xx responses shouldn't create any errors +func TestPingBadResponseCode(t *testing.T) { + testPingResponse(t, badGatewayHandler, http.StatusBadGateway, badGatewayBody) +} + +// TestPingBadHost tests a non-routable IP to ensure an error comes back +func TestPingBadHost(t *testing.T) { + client := http.DefaultClient + req, err := http.NewRequest("GET", "http://192.0.2.0", nil) + assert.Nil(t, err) + + validatorDidExecute := false + validator := func(resp *http.Response) error { + validatorDidExecute = true + return nil + } + + _, _, _, reason := execPing(client, req, nil, time.Second, validator) + + assert.False(t, validatorDidExecute) + assert.NotNil(t, reason) +} + +func TestPingBadValidator(t *testing.T) { + server := httptest.NewServer(helloWorldHandler) + defer server.Close() + + client := http.DefaultClient + req, err := http.NewRequest("GET", server.URL, nil) + assert.Nil(t, err) + + expectedError := fmt.Errorf("An Error") + + validator := func(resp *http.Response) error { + return expectedError + } + + _, _, _, reason := execPing(client, req, nil, time.Second, validator) + assert.NotNil(t, reason) + assert.Equal(t, expectedError.Error(), reason.Error()) } func TestSplitHostnamePort(t *testing.T) { From 1a2ab70f91ee556e09d40dcf0348485db0eda7c8 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Tue, 10 Jul 2018 14:56:49 -0500 Subject: [PATCH 06/14] Fix defer behavior --- heartbeat/monitors/active/http/task.go | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/heartbeat/monitors/active/http/task.go b/heartbeat/monitors/active/http/task.go index 15e635d320e..4bf444aaa6a 100644 --- a/heartbeat/monitors/active/http/task.go +++ b/heartbeat/monitors/active/http/task.go @@ -31,8 +31,6 @@ import ( "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/outputs/transport" - "io" - "github.com/elastic/beats/heartbeat/look" "github.com/elastic/beats/heartbeat/monitors" "github.com/elastic/beats/heartbeat/monitors/active/dialchain" @@ -225,11 +223,10 @@ func execPing( defer cancel() req = attachRequestBody(&ctx, req, body) - defer closeIfPresent(&req.Body) start, end, resp, errReason := execRequest(client, req, validator) if resp != nil { // If above errors, the response will be nil - defer closeIfPresent(&resp.Body) + defer resp.Body.Close() } if errReason != nil { return start, end, nil, errReason @@ -240,12 +237,6 @@ func execPing( return start, end, event, nil } -func closeIfPresent(closer *io.ReadCloser) { - if *closer != nil { - (*closer).Close() - } -} - func attachRequestBody(ctx *context.Context, req *http.Request, body []byte) *http.Request { req = req.WithContext(*ctx) if len(body) > 0 { From a664722650e6ef5ff15573dc6f133fcbaaf3857a Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Tue, 10 Jul 2018 15:00:19 -0500 Subject: [PATCH 07/14] Incorporate PR feedback --- heartbeat/monitors/active/http/task.go | 8 ++++---- heartbeat/monitors/active/http/task_test.go | 17 ++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/heartbeat/monitors/active/http/task.go b/heartbeat/monitors/active/http/task.go index 4bf444aaa6a..dd49cf1a887 100644 --- a/heartbeat/monitors/active/http/task.go +++ b/heartbeat/monitors/active/http/task.go @@ -223,11 +223,8 @@ func execPing( defer cancel() req = attachRequestBody(&ctx, req, body) - start, end, resp, errReason := execRequest(client, req, validator) - if resp != nil { // If above errors, the response will be nil - defer resp.Body.Close() - } + if errReason != nil { return start, end, nil, errReason } @@ -250,6 +247,9 @@ func attachRequestBody(ctx *context.Context, req *http.Request, body []byte) *ht func execRequest(client *http.Client, req *http.Request, validator func(*http.Response) error) (start time.Time, end time.Time, resp *http.Response, errReason reason.Reason) { start = time.Now() resp, err := client.Do(req) + if resp != nil { // If above errors, the response will be nil + defer resp.Body.Close() + } end = time.Now() if err != nil { diff --git a/heartbeat/monitors/active/http/task_test.go b/heartbeat/monitors/active/http/task_test.go index 1646d995f49..b6065d18eb3 100644 --- a/heartbeat/monitors/active/http/task_test.go +++ b/heartbeat/monitors/active/http/task_test.go @@ -18,22 +18,17 @@ package http import ( + "fmt" + "io" + "io/ioutil" "net" "net/http" + "net/http/httptest" "net/url" "reflect" "testing" - - "net/http/httptest" - "time" - "io/ioutil" - - "io" - - "fmt" - "github.com/stretchr/testify/assert" "github.com/elastic/beats/heartbeat/reason" @@ -62,7 +57,7 @@ func testPingResponse(t *testing.T, handlerFunc http.HandlerFunc, expectedStatus req, err := http.NewRequest("GET", server.URL, nil) assert.Nil(t, err) - var validatorResp *http.Response = nil + var validatorResp *http.Response var validatorBodyBytes []byte validator := func(resp *http.Response) error { validatorResp = resp @@ -201,7 +196,7 @@ func TestSplitHostnamePort(t *testing.T) { for _, test := range urlTests { test := test - t.Run(test.name, func(t2 *testing.T) { + t.Run(test.name, func(t *testing.T) { url := &url.URL{ Scheme: test.scheme, Host: test.host, From 73c946e7dbe22ec7d8e9157850c7b5c094be8171 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Tue, 10 Jul 2018 17:46:03 -0500 Subject: [PATCH 08/14] Checkpoint --- heartbeat/monitors/active/http/http_test.go | 240 ++++++++++++++++++++ heartbeat/monitors/active/http/task_test.go | 15 -- 2 files changed, 240 insertions(+), 15 deletions(-) create mode 100644 heartbeat/monitors/active/http/http_test.go diff --git a/heartbeat/monitors/active/http/http_test.go b/heartbeat/monitors/active/http/http_test.go new file mode 100644 index 00000000000..1b50b48e232 --- /dev/null +++ b/heartbeat/monitors/active/http/http_test.go @@ -0,0 +1,240 @@ +package http + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/heartbeat/monitors" + "github.com/elastic/beats/libbeat/beat" + "github.com/elastic/beats/libbeat/common" +) + +var helloWorldBody = "hello, world!" + +var helloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + io.WriteString(w, helloWorldBody) +}) + +var badGatewayBody = "Bad Gateway" + +var badGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadGateway) + io.WriteString(w, badGatewayBody) +}) + +var exactlyEqual = func(expected interface{}) func(t *testing.T, actual interface{}) { + return func(t *testing.T, actual interface{}) { + assert.Equal(t, expected, actual) + } +} + +var isDuration = func(t *testing.T, actual interface{}) { + converted, ok := actual.(time.Duration) + assert.True(t, ok) + assert.True(t, converted >= 0) +} + +var isNil = func(t *testing.T, actual interface{}) { + assert.Nil(t, actual) +} + +var isString = func(t *testing.T, actual interface{}) { + _, ok := actual.(string) + assert.True(t, ok) +} + +type eventFieldTest struct { + key string + description string + assertion func(t *testing.T, actual interface{}) +} + +func testEventFields(t *testing.T, event beat.Event, eventFieldTests []eventFieldTest) { + for _, eventFieldTest := range eventFieldTests { + matcher := eventFieldTest + name := fmt.Sprintf("%s %s", matcher.key, matcher.description) + t.Run(name, func(t *testing.T) { + actual, _ := event.Fields.GetValue(matcher.key) // ignore err to allow nil assert later + matcher.assertion(t, actual) + }) + } +} + +func executeHTTPMonitorHostJob(t *testing.T, handlerFunc http.HandlerFunc, expectedStatus int, expectedBody string, expectedMonitorStatus string) { + server := httptest.NewServer(handlerFunc) + defer server.Close() + + config := common.NewConfig() + config.SetString("urls", 0, server.URL) + + jobs, err := create(monitors.Info{}, config) + assert.Nil(t, err) + assert.Equal(t, 1, len(jobs)) + job := jobs[0] + + event, jobRunners, err := job.Run() + assert.Nil(t, err) + assert.NotNil(t, jobRunners) + + parsedServerURL, err := url.Parse(server.URL) + assert.Nil(t, err) + serverPortInt, err := strconv.Atoi(parsedServerURL.Port()) + assert.Nil(t, err) + serverPort := uint16(serverPortInt) + + fmt.Println(event.Fields) + testEventFields(t, event, []eventFieldTest{ + { + "tcp.port", + "is the server port", + exactlyEqual(serverPort), + }, + { + "tcp.rtt.connect.us", + "isDuration", + isDuration, + }, + { + "monitor.id", + "is the server URL and actual proto", + exactlyEqual("http@" + server.URL), + }, + { + "monitor.ip", + "is a string", // Don't test the exact value, could shift with IPv6 only stack + isString, + }, + { + "monitor.status", + "is up", + exactlyEqual(expectedMonitorStatus), + }, + { + "monitor.scheme", + "is http", + exactlyEqual("http"), + }, + { + "http.response.status_code", + "is as expected", + exactlyEqual(expectedStatus), + }, + { + "http.url", + "is the server URL", + exactlyEqual(server.URL), + }, + { + "http.rtt.content.us", + "isDuration", + isDuration, + }, + { + "http.rtt.response_header.us", + "isDuration", + isDuration, + }, + { + "http.rtt.total.us", + "isDuration", + isDuration, + }, + { + "http.rtt.validate.us", + "isDuration", + isDuration, + }, + { + "http.rtt.write_request.us", + "isDuration", + isDuration, + }, + }) +} + +func TestOKJob(t *testing.T) { + executeHTTPMonitorHostJob(t, helloWorldHandler, http.StatusOK, helloWorldBody, "up") +} + +func TestBadGatewayJob(t *testing.T) { + executeHTTPMonitorHostJob(t, badGatewayHandler, http.StatusBadGateway, badGatewayBody, "down") +} + +func TestBadHostJob(t *testing.T) { + config := common.NewConfig() + ip := "192.0.2.0" + url := "http://" + ip + config.SetString("urls", 0, url) + + jobs, err := create(monitors.Info{}, config) + assert.Nil(t, err) + assert.Equal(t, 1, len(jobs)) + job := jobs[0] + + event, jobRunners, err := job.Run() + assert.Nil(t, err) + assert.NotNil(t, jobRunners) + + fmt.Println(event.Fields) + testEventFields(t, event, []eventFieldTest{ + { + "error.message", + "is a string", + isString, + }, + { + "error.type", + "is io", + exactlyEqual("io"), + }, + { + "http.url", + "is the url", + exactlyEqual(url), + }, + { + "monitor.id", + "is the server URL and actual proto", + exactlyEqual("http@" + url), + }, + { + "monitor.ip", + "is the exact ip", + exactlyEqual(ip), + }, + { + "monitor.duration.us", + "is a duration", + isDuration, + }, + { + "monitor.status", + "is down", + exactlyEqual("down"), + }, + { + "monitor.scheme", + "is http", + exactlyEqual("http"), + }, + { + "http.rtt", + "isDuration", + isNil, + }, + { + "tcp.port", + "is port 80", + exactlyEqual(uint16(80)), + }, + }) +} diff --git a/heartbeat/monitors/active/http/task_test.go b/heartbeat/monitors/active/http/task_test.go index b6065d18eb3..ce7dc97d229 100644 --- a/heartbeat/monitors/active/http/task_test.go +++ b/heartbeat/monitors/active/http/task_test.go @@ -19,7 +19,6 @@ package http import ( "fmt" - "io" "io/ioutil" "net" "net/http" @@ -35,20 +34,6 @@ import ( "github.com/elastic/beats/libbeat/common" ) -var helloWorldBody = "hello, world!" - -var helloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - io.WriteString(w, helloWorldBody) -}) - -var badGatewayBody = "Bad Gateway" - -var badGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusBadGateway) - io.WriteString(w, badGatewayBody) -}) - func testPingResponse(t *testing.T, handlerFunc http.HandlerFunc, expectedStatus int, expectedBody string) (start time.Time, end time.Time, event common.MapStr, reason reason.Reason) { server := httptest.NewServer(handlerFunc) defer server.Close() From 064bda6ce4d9e8f8d9f4a34ad9cc177c3e6e8c92 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Wed, 11 Jul 2018 14:12:55 -0500 Subject: [PATCH 09/14] Checkpoint --- heartbeat/monitors/active/http/http_test.go | 248 ++++---------------- heartbeat/monitors/active/http/task_test.go | 7 +- heartbeat/monitors/active/tcp/tcp_test.go | 39 +++ heartbeat/testcommon/testcommon.go | 124 ++++++++++ 4 files changed, 212 insertions(+), 206 deletions(-) create mode 100644 heartbeat/monitors/active/tcp/tcp_test.go create mode 100644 heartbeat/testcommon/testcommon.go diff --git a/heartbeat/monitors/active/http/http_test.go b/heartbeat/monitors/active/http/http_test.go index 1b50b48e232..7da21fb675b 100644 --- a/heartbeat/monitors/active/http/http_test.go +++ b/heartbeat/monitors/active/http/http_test.go @@ -1,75 +1,19 @@ package http import ( - "fmt" - "io" "net/http" "net/http/httptest" - "net/url" - "strconv" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/elastic/beats/heartbeat/monitors" + "github.com/elastic/beats/heartbeat/testcommon" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" ) -var helloWorldBody = "hello, world!" - -var helloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - io.WriteString(w, helloWorldBody) -}) - -var badGatewayBody = "Bad Gateway" - -var badGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusBadGateway) - io.WriteString(w, badGatewayBody) -}) - -var exactlyEqual = func(expected interface{}) func(t *testing.T, actual interface{}) { - return func(t *testing.T, actual interface{}) { - assert.Equal(t, expected, actual) - } -} - -var isDuration = func(t *testing.T, actual interface{}) { - converted, ok := actual.(time.Duration) - assert.True(t, ok) - assert.True(t, converted >= 0) -} - -var isNil = func(t *testing.T, actual interface{}) { - assert.Nil(t, actual) -} - -var isString = func(t *testing.T, actual interface{}) { - _, ok := actual.(string) - assert.True(t, ok) -} - -type eventFieldTest struct { - key string - description string - assertion func(t *testing.T, actual interface{}) -} - -func testEventFields(t *testing.T, event beat.Event, eventFieldTests []eventFieldTest) { - for _, eventFieldTest := range eventFieldTests { - matcher := eventFieldTest - name := fmt.Sprintf("%s %s", matcher.key, matcher.description) - t.Run(name, func(t *testing.T) { - actual, _ := event.Fields.GetValue(matcher.key) // ignore err to allow nil assert later - matcher.assertion(t, actual) - }) - } -} - -func executeHTTPMonitorHostJob(t *testing.T, handlerFunc http.HandlerFunc, expectedStatus int, expectedBody string, expectedMonitorStatus string) { +func executeHTTPMonitorHostJob(t *testing.T, handlerFunc http.HandlerFunc) (*httptest.Server, beat.Event) { server := httptest.NewServer(handlerFunc) defer server.Close() @@ -77,164 +21,62 @@ func executeHTTPMonitorHostJob(t *testing.T, handlerFunc http.HandlerFunc, expec config.SetString("urls", 0, server.URL) jobs, err := create(monitors.Info{}, config) - assert.Nil(t, err) - assert.Equal(t, 1, len(jobs)) + if err != nil { + t.FailNow() + } job := jobs[0] - event, jobRunners, err := job.Run() - assert.Nil(t, err) - assert.NotNil(t, jobRunners) + event, _, err := job.Run() - parsedServerURL, err := url.Parse(server.URL) - assert.Nil(t, err) - serverPortInt, err := strconv.Atoi(parsedServerURL.Port()) - assert.Nil(t, err) - serverPort := uint16(serverPortInt) + return server, event +} - fmt.Println(event.Fields) - testEventFields(t, event, []eventFieldTest{ - { - "tcp.port", - "is the server port", - exactlyEqual(serverPort), - }, - { - "tcp.rtt.connect.us", - "isDuration", - isDuration, - }, - { - "monitor.id", - "is the server URL and actual proto", - exactlyEqual("http@" + server.URL), - }, - { - "monitor.ip", - "is a string", // Don't test the exact value, could shift with IPv6 only stack - isString, - }, - { - "monitor.status", - "is up", - exactlyEqual(expectedMonitorStatus), - }, - { - "monitor.scheme", - "is http", - exactlyEqual("http"), +func httpChecks(urlStr string, statusCode int) testcommon.MapCheckDef { + return testcommon.MapCheckDef{ + "http": testcommon.MapCheckDef{ + "url": urlStr, + "response.status_code": statusCode, + "rtt.content.us": testcommon.IsDuration, + "rtt.response_header.us": testcommon.IsDuration, + "rtt.total.us": testcommon.IsDuration, + "rtt.validate.us": testcommon.IsDuration, + "rtt.write_request.us": testcommon.IsDuration, }, - { - "http.response.status_code", - "is as expected", - exactlyEqual(expectedStatus), - }, - { - "http.url", - "is the server URL", - exactlyEqual(server.URL), - }, - { - "http.rtt.content.us", - "isDuration", - isDuration, - }, - { - "http.rtt.response_header.us", - "isDuration", - isDuration, - }, - { - "http.rtt.total.us", - "isDuration", - isDuration, - }, - { - "http.rtt.validate.us", - "isDuration", - isDuration, + } +} + +func httpErrorChecks(urlStr string, statusCode int) testcommon.MapCheckDef { + return testcommon.MapCheckDef{ + "error": testcommon.MapCheckDef{ + "message": "502 Bad Gateway", + "type": "validate", }, - { - "http.rtt.write_request.us", - "isDuration", - isDuration, + "http": testcommon.MapCheckDef{ + "url": urlStr, + "rtt.content.us": testcommon.IsDuration, + "rtt.response_header.us": testcommon.IsDuration, + "rtt.validate.us": testcommon.IsDuration, + "rtt.write_request.us": testcommon.IsDuration, }, - }) + } } func TestOKJob(t *testing.T) { - executeHTTPMonitorHostJob(t, helloWorldHandler, http.StatusOK, helloWorldBody, "up") -} + server, event := executeHTTPMonitorHostJob(t, testcommon.HelloWorldHandler) + port, err := testcommon.ServerPort(server) + assert.Nil(t, err) -func TestBadGatewayJob(t *testing.T) { - executeHTTPMonitorHostJob(t, badGatewayHandler, http.StatusBadGateway, badGatewayBody, "down") + testcommon.DeepMapStrCheck(t, testcommon.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "up"), event.Fields) + testcommon.DeepMapStrCheck(t, testcommon.TcpChecks(port), event.Fields) + testcommon.DeepMapStrCheck(t, httpChecks(server.URL, http.StatusOK), event.Fields) } -func TestBadHostJob(t *testing.T) { - config := common.NewConfig() - ip := "192.0.2.0" - url := "http://" + ip - config.SetString("urls", 0, url) - - jobs, err := create(monitors.Info{}, config) - assert.Nil(t, err) - assert.Equal(t, 1, len(jobs)) - job := jobs[0] - - event, jobRunners, err := job.Run() +func TestBadGatewayJob(t *testing.T) { + server, event := executeHTTPMonitorHostJob(t, testcommon.BadGatewayHandler) + port, err := testcommon.ServerPort(server) assert.Nil(t, err) - assert.NotNil(t, jobRunners) - fmt.Println(event.Fields) - testEventFields(t, event, []eventFieldTest{ - { - "error.message", - "is a string", - isString, - }, - { - "error.type", - "is io", - exactlyEqual("io"), - }, - { - "http.url", - "is the url", - exactlyEqual(url), - }, - { - "monitor.id", - "is the server URL and actual proto", - exactlyEqual("http@" + url), - }, - { - "monitor.ip", - "is the exact ip", - exactlyEqual(ip), - }, - { - "monitor.duration.us", - "is a duration", - isDuration, - }, - { - "monitor.status", - "is down", - exactlyEqual("down"), - }, - { - "monitor.scheme", - "is http", - exactlyEqual("http"), - }, - { - "http.rtt", - "isDuration", - isNil, - }, - { - "tcp.port", - "is port 80", - exactlyEqual(uint16(80)), - }, - }) + testcommon.DeepMapStrCheck(t, testcommon.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "down"), event.Fields) + testcommon.DeepMapStrCheck(t, testcommon.TcpChecks(port), event.Fields) + testcommon.DeepMapStrCheck(t, httpErrorChecks(server.URL, http.StatusBadGateway), event.Fields) } diff --git a/heartbeat/monitors/active/http/task_test.go b/heartbeat/monitors/active/http/task_test.go index ce7dc97d229..b5344a27dc9 100644 --- a/heartbeat/monitors/active/http/task_test.go +++ b/heartbeat/monitors/active/http/task_test.go @@ -31,6 +31,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/elastic/beats/heartbeat/reason" + "github.com/elastic/beats/heartbeat/testcommon" "github.com/elastic/beats/libbeat/common" ) @@ -66,12 +67,12 @@ func testPingResponse(t *testing.T, handlerFunc http.HandlerFunc, expectedStatus } func TestGoodResponseCode(t *testing.T) { - testPingResponse(t, helloWorldHandler, http.StatusOK, helloWorldBody) + testPingResponse(t, testcommon.HelloWorldHandler, http.StatusOK, testcommon.HelloWorldBody) } // Non 2xx responses shouldn't create any errors func TestPingBadResponseCode(t *testing.T) { - testPingResponse(t, badGatewayHandler, http.StatusBadGateway, badGatewayBody) + testPingResponse(t, testcommon.BadGatewayHandler, http.StatusBadGateway, testcommon.BadGatewayBody) } // TestPingBadHost tests a non-routable IP to ensure an error comes back @@ -93,7 +94,7 @@ func TestPingBadHost(t *testing.T) { } func TestPingBadValidator(t *testing.T) { - server := httptest.NewServer(helloWorldHandler) + server := httptest.NewServer(testcommon.HelloWorldHandler) defer server.Close() client := http.DefaultClient diff --git a/heartbeat/monitors/active/tcp/tcp_test.go b/heartbeat/monitors/active/tcp/tcp_test.go new file mode 100644 index 00000000000..14bd5f905cf --- /dev/null +++ b/heartbeat/monitors/active/tcp/tcp_test.go @@ -0,0 +1,39 @@ +package tcp + +import ( + "fmt" + "net/http/httptest" + "testing" + + "github.com/elastic/beats/heartbeat/monitors" + "github.com/elastic/beats/heartbeat/testcommon" + "github.com/elastic/beats/libbeat/common" +) + +func TestUpEndpoint(t *testing.T) { + server := httptest.NewServer(testcommon.HelloWorldHandler) + defer server.Close() + + port, err := testcommon.ServerPort(server) + if err != nil { + t.FailNow() + } + + config := common.NewConfig() + config.SetString("hosts", 0, "localhost") + config.SetInt("ports", 0, int64(port)) + + jobs, err := create(monitors.Info{}, config) + if err != nil { + t.FailNow() + } + job := jobs[0] + + event, _, err := job.Run() + if err != nil { + t.FailNow() + } + + testcommon.DeepMapStrCheck(t, testcommon.MonitorChecks(fmt.Sprintf("tcp-tcp@localhost:%d", port), "127.0.0.1", "tcp", "up"), event.Fields) + testcommon.DeepMapStrCheck(t, testcommon.TcpChecks(port), event.Fields) +} diff --git a/heartbeat/testcommon/testcommon.go b/heartbeat/testcommon/testcommon.go new file mode 100644 index 00000000000..cd75568a8d4 --- /dev/null +++ b/heartbeat/testcommon/testcommon.go @@ -0,0 +1,124 @@ +package testcommon + +import ( + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "fmt" + + "io" + "net/http" + + "net/http/httptest" + "net/url" + "strconv" + + "github.com/elastic/beats/libbeat/common" +) + +var HelloWorldBody = "hello, world!" + +var HelloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + io.WriteString(w, HelloWorldBody) +}) + +var BadGatewayBody = "Bad Gateway" + +var BadGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadGateway) + io.WriteString(w, BadGatewayBody) +}) + +var ExactlyEqual = func(expected interface{}) func(t *testing.T, actual interface{}) { + return func(t *testing.T, actual interface{}) { + assert.Equal(t, expected, actual) + } +} + +func ServerPort(server *httptest.Server) (uint16, error) { + u, err := url.Parse(server.URL) + if err != nil { + return 0, err + } + p, err := strconv.Atoi(u.Port()) + if err != nil { + return 0, err + } + return uint16(p), nil +} + +// Functions for testing maps in complex ways + +func MonitorChecks(id string, ip string, scheme string, status string) MapCheckDef { + return MapCheckDef{ + "monitor": MapCheckDef{ + "duration.us": IsDuration, + "id": id, + "ip": ip, + "scheme": scheme, + "status": status, + }, + } +} + +func TcpChecks(port uint16) MapCheckDef { + return MapCheckDef{ + "tcp": MapCheckDef{ + "port": port, + "rtt.connect.us": IsDuration, + }, + } +} + +var IsDuration = func(t *testing.T, actual interface{}) { + converted, ok := actual.(time.Duration) + assert.True(t, ok) + assert.True(t, converted >= 0) +} + +var IsNil = func(t *testing.T, actual interface{}) { + assert.Nil(t, actual) +} + +var IsString = func(t *testing.T, actual interface{}) { + _, ok := actual.(string) + assert.True(t, ok) +} + +type MapLeafTest = func(t *testing.T, actual interface{}) + +type MapCheckDef common.MapStr + +func DeepMapStrCheck(t *testing.T, expected MapCheckDef, actual common.MapStr) { + deepMapStrCheckPath(t, expected, actual, actual, []string{}) +} + +func deepMapStrCheckPath(t *testing.T, expected MapCheckDef, actual common.MapStr, rootActual common.MapStr, path []string) { + fmt.Printf("A: %v\n", actual) + for expectedK, expectedV := range expected { + expectedV := expectedV // Bind locally for subsequent t.Run calls + keyPath := strings.Join(path, ".") + "." + expectedK + mapLeafTest, ok := expectedV.(MapLeafTest) + actualV, err := actual.GetValue(expectedK) + assert.Nil(t, err, fmt.Sprintf("Expectation exists for %s', but the given MapStr does not have it: %v", keyPath, rootActual)) + if ok { + t.Run(fmt.Sprintf("map path|customMatch(%s)", keyPath), func(t *testing.T) { mapLeafTest(t, actualV) }) + } else if actualVMapStr, nestedOK := actualV.(common.MapStr); nestedOK { + expectedVMapStr, ok := expectedV.(MapCheckDef) + if ok != true { + t.FailNow() + } + assert.True(t, ok) + deepMapStrCheckPath(t, expectedVMapStr, actualVMapStr, rootActual, append(path, expectedK)) + } else { + // assert exact equality otherwise + t.Run(fmt.Sprintf("map path|%s=>%v", keyPath, expectedV), func(t *testing.T) { + assert.Equal(t, expectedV, actualV, "Expected %s to equal '%v', but got '%v'", keyPath, expectedV, actualV) + }) + } + } +} From e8eb5aa07161d4e3ac0817262b15db5882621aba Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Wed, 11 Jul 2018 17:58:17 -0500 Subject: [PATCH 10/14] Checkpoint --- heartbeat/mapscheme/mapscheme.go | 247 ++++ heartbeat/mapscheme/mapscheme_test.go | 1 + heartbeat/monitors/active/http/http_test.go | 57 +- heartbeat/monitors/active/http/task_test.go | 86 -- heartbeat/monitors/active/tcp/tcp_test.go | 10 +- heartbeat/testcommon/testcommon.go | 124 -- .../stretchr/testify/require/doc.go | 28 + .../testify/require/forward_requirements.go | 16 + .../stretchr/testify/require/require.go | 1227 +++++++++++++++++ .../stretchr/testify/require/require.go.tmpl | 6 + .../testify/require/require_forward.go | 957 +++++++++++++ .../testify/require/require_forward.go.tmpl | 5 + .../stretchr/testify/require/requirements.go | 29 + vendor/vendor.json | 6 + 14 files changed, 2556 insertions(+), 243 deletions(-) create mode 100644 heartbeat/mapscheme/mapscheme.go create mode 100644 heartbeat/mapscheme/mapscheme_test.go delete mode 100644 heartbeat/testcommon/testcommon.go create mode 100644 vendor/github.com/stretchr/testify/require/doc.go create mode 100644 vendor/github.com/stretchr/testify/require/forward_requirements.go create mode 100644 vendor/github.com/stretchr/testify/require/require.go create mode 100644 vendor/github.com/stretchr/testify/require/require.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/require/require_forward.go create mode 100644 vendor/github.com/stretchr/testify/require/require_forward.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/require/requirements.go diff --git a/heartbeat/mapscheme/mapscheme.go b/heartbeat/mapscheme/mapscheme.go new file mode 100644 index 00000000000..0470d8a7292 --- /dev/null +++ b/heartbeat/mapscheme/mapscheme.go @@ -0,0 +1,247 @@ +package mapscheme + +import ( + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "fmt" + + "io" + "net/http" + + "net/http/httptest" + "net/url" + "strconv" + + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/libbeat/common" +) + +var HelloWorldBody = "hello, world!" + +var HelloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + io.WriteString(w, HelloWorldBody) +}) + +var BadGatewayBody = "Bad Gateway" + +var BadGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadGateway) + io.WriteString(w, BadGatewayBody) +}) + +var ExactlyEqual = func(expected interface{}) func(t *testing.T, actual interface{}) { + return func(t *testing.T, actual interface{}) { + assert.Equal(t, expected, actual) + } +} + +func ServerPort(server *httptest.Server) (uint16, error) { + u, err := url.Parse(server.URL) + if err != nil { + return 0, err + } + p, err := strconv.Atoi(u.Port()) + if err != nil { + return 0, err + } + return uint16(p), nil +} + +// Functions for testing maps in complex ways + +func MonitorChecks(id string, ip string, scheme string, status string) MapCheckDef { + return MapCheckDef{ + "monitor": MapCheckDef{ + "duration.us": IsDuration, + "id": id, + "ip": ip, + "scheme": scheme, + "status": status, + }, + } +} + +func TcpChecks(port uint16) MapCheckDef { + return MapCheckDef{ + "tcp": MapCheckDef{ + "port": port, + "rtt.connect.us": IsDuration, + }, + } +} + +var IsDuration = func(t *testing.T, _ bool, actual interface{}) { + converted, ok := actual.(time.Duration) + assert.True(t, ok) + assert.True(t, converted >= 0) +} + +var IsNil = func(t *testing.T, _ bool, actual interface{}) { + assert.Nil(t, actual) +} + +var IsString = func(t *testing.T, _ bool, actual interface{}) { + _, ok := actual.(string) + assert.True(t, ok) +} + +type ValueValidator = func(t *testing.T, keyExists bool, actual interface{}) + +type MapCheckDef map[string]interface{} + +type Validator func(*testing.T, common.MapStr) *ValidationResult + +type ValidationResult struct { + expectedFields map[string]struct{} + unexpectedFields map[string]struct{} +} + +func combine(results []*ValidationResult) *ValidationResult { + // Use sets to de-dupe these + output := ValidationResult{} + + for _, res := range results { + for k, _ := range res.expectedFields { + output.expectedFields[k] = struct{}{} + } + } + + for _, res := range results { + for k := range res.unexpectedFields { + // Unexpected fields are those that are in no expectedFields + // for any test + if _, ok := output.expectedFields[k]; ok == false { + output.unexpectedFields[k] = struct{}{} + } + } + } + + return &output +} + +func Compose(validators ...Validator) Validator { + return func(t *testing.T, actual common.MapStr) *ValidationResult { + results := make([]*ValidationResult, len(validators)) + for _, validator := range validators { + result := validator(t, actual) + results = append(results, result) + } + return combine(results) + } +} + +func Strict(validator Validator) Validator { + return func(t *testing.T, actual common.MapStr) *ValidationResult { + res := validator(t, actual) + + return res + } +} + +func Scheme(expected MapCheckDef) Validator { + return func(t *testing.T, actual common.MapStr) *ValidationResult { + return Validate(t, expected, actual) + } +} + +func Validate(t *testing.T, expected MapCheckDef, actual common.MapStr) *ValidationResult { + return validateInternal(t, expected, actual, actual, []string{}) +} + +type WalkObserver func( + key string, + value interface{}, + currentMap common.MapStr, + rootMap common.MapStr, + path []string, + dottedPath string, +) + +func walk(m common.MapStr, wo WalkObserver) { + walkFull(m, m, []string{}, wo) +} + +// TODO: Handle slices/arrays. We intentionally don't handle list types now because we don't need it (yet) +// and it isn't clear in the context of validation what the right thing is to do there beyond letting the user +// perform a custom validation +func walkFull(m common.MapStr, root common.MapStr, path []string, wo WalkObserver) { + for k, v := range m { + wo(k, v, m, root, path, strings.Join(path, ".")) + + // Walk nested maps + if mapV, ok := v.(common.MapStr); ok { + newPath := make([]string, len(path)+1) + copy(newPath, path) + newPath[len(path)] = k // Append the key + walkFull(mapV, root, newPath, wo) + } + } +} + +func walkValidate(t *testing.T, expected MapCheckDef, actual common.MapStr) (output *ValidationResult) { + walk( + common.MapStr(expected), + func(expectedK string, + expectedV interface{}, + currentMap common.MapStr, + rootMap common.MapStr, + path []string, + dottedPath string) { + actualHasKey, err := actual.HasKey(dottedPath) + require.Nil(t, err) + actualV, err := actual.GetValue(dottedPath) + require.Nil(t, err) + + vv, isVV := expectedV.(ValueValidator) + if isVV { + t.Run(fmt.Sprintf("map path|customMatch(%s)", dottedPath), func(t *testing.T) { vv(t, actualHasKey, actualV) }) + } else { // Assert exact equality + t.Run(fmt.Sprintf("map path|%s=>%v", dottedPath, expectedV), func(t *testing.T) { + assert.Equal(t, expectedV, actualV, "Expected %s to equal '%v', but got '%v'", dottedPath, expectedV, actualV) + }) + } + + }) +} + +func validateInternal(t *testing.T, expected MapCheckDef, actual common.MapStr, rootActual common.MapStr, path []string) (output *ValidationResult) { + fmt.Printf("A: %v\n", actual) + + for expectedK, expectedV := range expected { + expectedV := expectedV // Bind locally for subsequent t.Run calls + keyPath := strings.Join(path, ".") + "." + expectedK + mapLeafTest, ok := expectedV.(ValueValidator) + + hasKey, err := actual.HasKey(expectedK) + require.Nil(t, err) + require.True(t, hasKey, fmt.Sprintf("Expectation exists for %s', but the given MapStr does not have it: %v", keyPath, rootActual)) + if hasKey { + output.expectedFields[keyPath] = struct{}{} + } + actualV, _ := actual.GetValue(expectedK) + + if ok { + t.Run(fmt.Sprintf("map path|customMatch(%s)", keyPath), func(t *testing.T) { mapLeafTest(t, actualV) }) + } else if actualVMapStr, nestedOK := actualV.(common.MapStr); nestedOK { + expectedVMapStr, ok := expectedV.(MapCheckDef) + if ok != true { + t.FailNow() + } + assert.True(t, ok) + validateInternal(t, expectedVMapStr, actualVMapStr, rootActual, append(path, expectedK)) + } else { + // assert exact equality otherwise + t.Run(fmt.Sprintf("map path|%s=>%v", keyPath, expectedV), func(t *testing.T) { + assert.Equal(t, expectedV, actualV, "Expected %s to equal '%v', but got '%v'", keyPath, expectedV, actualV) + }) + } + } + + return output +} diff --git a/heartbeat/mapscheme/mapscheme_test.go b/heartbeat/mapscheme/mapscheme_test.go new file mode 100644 index 00000000000..f794273fb96 --- /dev/null +++ b/heartbeat/mapscheme/mapscheme_test.go @@ -0,0 +1 @@ +package mapscheme diff --git a/heartbeat/monitors/active/http/http_test.go b/heartbeat/monitors/active/http/http_test.go index 7da21fb675b..a22ad44ae70 100644 --- a/heartbeat/monitors/active/http/http_test.go +++ b/heartbeat/monitors/active/http/http_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/assert" + "github.com/elastic/beats/heartbeat/mapscheme" "github.com/elastic/beats/heartbeat/monitors" - "github.com/elastic/beats/heartbeat/testcommon" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" ) @@ -31,52 +31,53 @@ func executeHTTPMonitorHostJob(t *testing.T, handlerFunc http.HandlerFunc) (*htt return server, event } -func httpChecks(urlStr string, statusCode int) testcommon.MapCheckDef { - return testcommon.MapCheckDef{ - "http": testcommon.MapCheckDef{ +func httpChecks(urlStr string, statusCode int) mapscheme.MapCheckDef { + return mapscheme.MapCheckDef{ + "http": mapscheme.MapCheckDef{ "url": urlStr, "response.status_code": statusCode, - "rtt.content.us": testcommon.IsDuration, - "rtt.response_header.us": testcommon.IsDuration, - "rtt.total.us": testcommon.IsDuration, - "rtt.validate.us": testcommon.IsDuration, - "rtt.write_request.us": testcommon.IsDuration, + "rtt.content.us": mapscheme.IsDuration, + "rtt.response_header.us": mapscheme.IsDuration, + "rtt.total.us": mapscheme.IsDuration, + "rtt.validate.us": mapscheme.IsDuration, + "rtt.write_request.us": mapscheme.IsDuration, }, } } -func httpErrorChecks(urlStr string, statusCode int) testcommon.MapCheckDef { - return testcommon.MapCheckDef{ - "error": testcommon.MapCheckDef{ +func httpErrorChecks(urlStr string, statusCode int) mapscheme.MapCheckDef { + return mapscheme.MapCheckDef{ + "error": mapscheme.MapCheckDef{ "message": "502 Bad Gateway", "type": "validate", }, - "http": testcommon.MapCheckDef{ - "url": urlStr, - "rtt.content.us": testcommon.IsDuration, - "rtt.response_header.us": testcommon.IsDuration, - "rtt.validate.us": testcommon.IsDuration, - "rtt.write_request.us": testcommon.IsDuration, + "http": mapscheme.MapCheckDef{ + "url": urlStr, + // TODO: This should work in the future "response.status_code": statusCode, + "rtt.content.us": mapscheme.IsDuration, + "rtt.response_header.us": mapscheme.IsDuration, + "rtt.validate.us": mapscheme.IsDuration, + "rtt.write_request.us": mapscheme.IsDuration, }, } } func TestOKJob(t *testing.T) { - server, event := executeHTTPMonitorHostJob(t, testcommon.HelloWorldHandler) - port, err := testcommon.ServerPort(server) + server, event := executeHTTPMonitorHostJob(t, mapscheme.HelloWorldHandler) + port, err := mapscheme.ServerPort(server) assert.Nil(t, err) - testcommon.DeepMapStrCheck(t, testcommon.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "up"), event.Fields) - testcommon.DeepMapStrCheck(t, testcommon.TcpChecks(port), event.Fields) - testcommon.DeepMapStrCheck(t, httpChecks(server.URL, http.StatusOK), event.Fields) + mapscheme.Validate(t, mapscheme.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "up"), event.Fields) + mapscheme.Validate(t, mapscheme.TcpChecks(port), event.Fields) + mapscheme.Validate(t, httpChecks(server.URL, http.StatusOK), event.Fields) } func TestBadGatewayJob(t *testing.T) { - server, event := executeHTTPMonitorHostJob(t, testcommon.BadGatewayHandler) - port, err := testcommon.ServerPort(server) + server, event := executeHTTPMonitorHostJob(t, mapscheme.BadGatewayHandler) + port, err := mapscheme.ServerPort(server) assert.Nil(t, err) - testcommon.DeepMapStrCheck(t, testcommon.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "down"), event.Fields) - testcommon.DeepMapStrCheck(t, testcommon.TcpChecks(port), event.Fields) - testcommon.DeepMapStrCheck(t, httpErrorChecks(server.URL, http.StatusBadGateway), event.Fields) + mapscheme.Validate(t, mapscheme.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "down"), event.Fields) + mapscheme.Validate(t, mapscheme.TcpChecks(port), event.Fields) + mapscheme.Validate(t, httpErrorChecks(server.URL, http.StatusBadGateway), event.Fields) } diff --git a/heartbeat/monitors/active/http/task_test.go b/heartbeat/monitors/active/http/task_test.go index b5344a27dc9..7e82eb4865b 100644 --- a/heartbeat/monitors/active/http/task_test.go +++ b/heartbeat/monitors/active/http/task_test.go @@ -18,101 +18,15 @@ package http import ( - "fmt" - "io/ioutil" "net" "net/http" - "net/http/httptest" "net/url" "reflect" "testing" - "time" "github.com/stretchr/testify/assert" - - "github.com/elastic/beats/heartbeat/reason" - "github.com/elastic/beats/heartbeat/testcommon" - "github.com/elastic/beats/libbeat/common" ) -func testPingResponse(t *testing.T, handlerFunc http.HandlerFunc, expectedStatus int, expectedBody string) (start time.Time, end time.Time, event common.MapStr, reason reason.Reason) { - server := httptest.NewServer(handlerFunc) - defer server.Close() - - client := http.DefaultClient - req, err := http.NewRequest("GET", server.URL, nil) - assert.Nil(t, err) - - var validatorResp *http.Response - var validatorBodyBytes []byte - validator := func(resp *http.Response) error { - validatorResp = resp - validatorBodyBytes, err = ioutil.ReadAll(resp.Body) - assert.Nil(t, err) - return nil - } - - start, end, event, reason = execPing(client, req, nil, time.Second, validator) - - assert.Equal(t, expectedStatus, validatorResp.StatusCode) - assert.Nil(t, err) - assert.Equal(t, expectedBody, string(validatorBodyBytes)) - - assert.Nil(t, reason) - assert.True(t, start.Before(end) || start.Equal(end)) - // More robust tests of the event can go at a higher level - assert.NotNil(t, event) - - return start, end, event, reason -} - -func TestGoodResponseCode(t *testing.T) { - testPingResponse(t, testcommon.HelloWorldHandler, http.StatusOK, testcommon.HelloWorldBody) -} - -// Non 2xx responses shouldn't create any errors -func TestPingBadResponseCode(t *testing.T) { - testPingResponse(t, testcommon.BadGatewayHandler, http.StatusBadGateway, testcommon.BadGatewayBody) -} - -// TestPingBadHost tests a non-routable IP to ensure an error comes back -func TestPingBadHost(t *testing.T) { - client := http.DefaultClient - req, err := http.NewRequest("GET", "http://192.0.2.0", nil) - assert.Nil(t, err) - - validatorDidExecute := false - validator := func(resp *http.Response) error { - validatorDidExecute = true - return nil - } - - _, _, _, reason := execPing(client, req, nil, time.Second, validator) - - assert.False(t, validatorDidExecute) - assert.NotNil(t, reason) -} - -func TestPingBadValidator(t *testing.T) { - server := httptest.NewServer(testcommon.HelloWorldHandler) - defer server.Close() - - client := http.DefaultClient - req, err := http.NewRequest("GET", server.URL, nil) - assert.Nil(t, err) - - expectedError := fmt.Errorf("An Error") - - validator := func(resp *http.Response) error { - return expectedError - } - - _, _, _, reason := execPing(client, req, nil, time.Second, validator) - - assert.NotNil(t, reason) - assert.Equal(t, expectedError.Error(), reason.Error()) -} - func TestSplitHostnamePort(t *testing.T) { var urlTests = []struct { name string diff --git a/heartbeat/monitors/active/tcp/tcp_test.go b/heartbeat/monitors/active/tcp/tcp_test.go index 14bd5f905cf..3288359a584 100644 --- a/heartbeat/monitors/active/tcp/tcp_test.go +++ b/heartbeat/monitors/active/tcp/tcp_test.go @@ -5,16 +5,16 @@ import ( "net/http/httptest" "testing" + "github.com/elastic/beats/heartbeat/mapscheme" "github.com/elastic/beats/heartbeat/monitors" - "github.com/elastic/beats/heartbeat/testcommon" "github.com/elastic/beats/libbeat/common" ) func TestUpEndpoint(t *testing.T) { - server := httptest.NewServer(testcommon.HelloWorldHandler) + server := httptest.NewServer(mapscheme.HelloWorldHandler) defer server.Close() - port, err := testcommon.ServerPort(server) + port, err := mapscheme.ServerPort(server) if err != nil { t.FailNow() } @@ -34,6 +34,6 @@ func TestUpEndpoint(t *testing.T) { t.FailNow() } - testcommon.DeepMapStrCheck(t, testcommon.MonitorChecks(fmt.Sprintf("tcp-tcp@localhost:%d", port), "127.0.0.1", "tcp", "up"), event.Fields) - testcommon.DeepMapStrCheck(t, testcommon.TcpChecks(port), event.Fields) + mapscheme.Validate(t, mapscheme.MonitorChecks(fmt.Sprintf("tcp-tcp@localhost:%d", port), "127.0.0.1", "tcp", "up"), event.Fields) + mapscheme.Validate(t, mapscheme.TcpChecks(port), event.Fields) } diff --git a/heartbeat/testcommon/testcommon.go b/heartbeat/testcommon/testcommon.go deleted file mode 100644 index cd75568a8d4..00000000000 --- a/heartbeat/testcommon/testcommon.go +++ /dev/null @@ -1,124 +0,0 @@ -package testcommon - -import ( - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "fmt" - - "io" - "net/http" - - "net/http/httptest" - "net/url" - "strconv" - - "github.com/elastic/beats/libbeat/common" -) - -var HelloWorldBody = "hello, world!" - -var HelloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - io.WriteString(w, HelloWorldBody) -}) - -var BadGatewayBody = "Bad Gateway" - -var BadGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusBadGateway) - io.WriteString(w, BadGatewayBody) -}) - -var ExactlyEqual = func(expected interface{}) func(t *testing.T, actual interface{}) { - return func(t *testing.T, actual interface{}) { - assert.Equal(t, expected, actual) - } -} - -func ServerPort(server *httptest.Server) (uint16, error) { - u, err := url.Parse(server.URL) - if err != nil { - return 0, err - } - p, err := strconv.Atoi(u.Port()) - if err != nil { - return 0, err - } - return uint16(p), nil -} - -// Functions for testing maps in complex ways - -func MonitorChecks(id string, ip string, scheme string, status string) MapCheckDef { - return MapCheckDef{ - "monitor": MapCheckDef{ - "duration.us": IsDuration, - "id": id, - "ip": ip, - "scheme": scheme, - "status": status, - }, - } -} - -func TcpChecks(port uint16) MapCheckDef { - return MapCheckDef{ - "tcp": MapCheckDef{ - "port": port, - "rtt.connect.us": IsDuration, - }, - } -} - -var IsDuration = func(t *testing.T, actual interface{}) { - converted, ok := actual.(time.Duration) - assert.True(t, ok) - assert.True(t, converted >= 0) -} - -var IsNil = func(t *testing.T, actual interface{}) { - assert.Nil(t, actual) -} - -var IsString = func(t *testing.T, actual interface{}) { - _, ok := actual.(string) - assert.True(t, ok) -} - -type MapLeafTest = func(t *testing.T, actual interface{}) - -type MapCheckDef common.MapStr - -func DeepMapStrCheck(t *testing.T, expected MapCheckDef, actual common.MapStr) { - deepMapStrCheckPath(t, expected, actual, actual, []string{}) -} - -func deepMapStrCheckPath(t *testing.T, expected MapCheckDef, actual common.MapStr, rootActual common.MapStr, path []string) { - fmt.Printf("A: %v\n", actual) - for expectedK, expectedV := range expected { - expectedV := expectedV // Bind locally for subsequent t.Run calls - keyPath := strings.Join(path, ".") + "." + expectedK - mapLeafTest, ok := expectedV.(MapLeafTest) - actualV, err := actual.GetValue(expectedK) - assert.Nil(t, err, fmt.Sprintf("Expectation exists for %s', but the given MapStr does not have it: %v", keyPath, rootActual)) - if ok { - t.Run(fmt.Sprintf("map path|customMatch(%s)", keyPath), func(t *testing.T) { mapLeafTest(t, actualV) }) - } else if actualVMapStr, nestedOK := actualV.(common.MapStr); nestedOK { - expectedVMapStr, ok := expectedV.(MapCheckDef) - if ok != true { - t.FailNow() - } - assert.True(t, ok) - deepMapStrCheckPath(t, expectedVMapStr, actualVMapStr, rootActual, append(path, expectedK)) - } else { - // assert exact equality otherwise - t.Run(fmt.Sprintf("map path|%s=>%v", keyPath, expectedV), func(t *testing.T) { - assert.Equal(t, expectedV, actualV, "Expected %s to equal '%v', but got '%v'", keyPath, expectedV, actualV) - }) - } - } -} diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go new file mode 100644 index 00000000000..169de39221c --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -0,0 +1,28 @@ +// Package require implements the same assertions as the `assert` package but +// stops test execution when a test fails. +// +// Example Usage +// +// The following is a complete example using require in a standard test function: +// import ( +// "testing" +// "github.com/stretchr/testify/require" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// require.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// Assertions +// +// The `require` package have same global functions as in the `assert` package, +// but instead of returning a boolean result they call `t.FailNow()`. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +package require diff --git a/vendor/github.com/stretchr/testify/require/forward_requirements.go b/vendor/github.com/stretchr/testify/require/forward_requirements.go new file mode 100644 index 00000000000..ac71d40581b --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/forward_requirements.go @@ -0,0 +1,16 @@ +package require + +// Assertions provides assertion methods around the +// TestingT interface. +type Assertions struct { + t TestingT +} + +// New makes a new Assertions object for the specified TestingT. +func New(t TestingT) *Assertions { + return &Assertions{ + t: t, + } +} + +//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl -include-format-funcs diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go new file mode 100644 index 00000000000..535f293490c --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -0,0 +1,1227 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package require + +import ( + assert "github.com/stretchr/testify/assert" + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { + if assert.Condition(t, comp, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Conditionf uses a Comparison to assert a complex condition. +func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interface{}) { + if assert.Conditionf(t, comp, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") +func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if assert.Contains(t, s, contains, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { + if assert.Containsf(t, s, contains, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { + if assert.DirExists(t, path, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { + if assert.DirExistsf(t, path, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { + if assert.ElementsMatchf(t, listA, listB, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Empty(t, obj) +func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if assert.Empty(t, object, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Emptyf(t, obj, "error message %s", "formatted") +func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { + if assert.Emptyf(t, object, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Equal asserts that two objects are equal. +// +// assert.Equal(t, 123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if assert.Equal(t, expected, actual, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { + if assert.EqualError(t, theError, errString, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { + if assert.EqualErrorf(t, theError, errString, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValues(t, uint32(123), int32(123)) +func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if assert.EqualValues(t, expected, actual, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// EqualValuesf asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) +func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if assert.EqualValuesf(t, expected, actual, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Equalf asserts that two objects are equal. +// +// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if assert.Equalf(t, expected, actual, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } +func Error(t TestingT, err error, msgAndArgs ...interface{}) { + if assert.Error(t, err, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } +func Errorf(t TestingT, err error, msg string, args ...interface{}) { + if assert.Errorf(t, err, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Exactly asserts that two objects are equal in value and type. +// +// assert.Exactly(t, int32(123), int64(123)) +func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if assert.Exactly(t, expected, actual, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) +func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if assert.Exactlyf(t, expected, actual, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if assert.Fail(t, failureMessage, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// FailNow fails test +func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if assert.FailNow(t, failureMessage, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// FailNowf fails test +func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) { + if assert.FailNowf(t, failureMessage, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Failf reports a failure through +func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { + if assert.Failf(t, failureMessage, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// False asserts that the specified value is false. +// +// assert.False(t, myBool) +func False(t TestingT, value bool, msgAndArgs ...interface{}) { + if assert.False(t, value, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Falsef asserts that the specified value is false. +// +// assert.Falsef(t, myBool, "error message %s", "formatted") +func Falsef(t TestingT, value bool, msg string, args ...interface{}) { + if assert.Falsef(t, value, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { + if assert.FileExists(t, path, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { + if assert.FileExistsf(t, path, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Implements asserts that an object is implemented by the specified interface. +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if assert.Implements(t, interfaceObject, object, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) +func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if assert.Implementsf(t, interfaceObject, object, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValues(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) +func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if assert.InDeltaf(t, expected, actual, delta, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// IsType asserts that the specified objects are of the same type. +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + if assert.IsType(t, expectedType, object, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// IsTypef asserts that the specified objects are of the same type. +func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { + if assert.IsTypef(t, expectedType, object, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if assert.JSONEq(t, expected, actual, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if assert.JSONEqf(t, expected, actual, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3) +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { + if assert.Len(t, object, length, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { + if assert.Lenf(t, object, length, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Nil asserts that the specified object is nil. +// +// assert.Nil(t, err) +func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if assert.Nil(t, object, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Nilf asserts that the specified object is nil. +// +// assert.Nilf(t, err, "error message %s", "formatted") +func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { + if assert.Nilf(t, object, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func NoError(t TestingT, err error, msgAndArgs ...interface{}) { + if assert.NoError(t, err, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { + if assert.NoErrorf(t, err, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") +func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if assert.NotContains(t, s, contains, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { + if assert.NotContainsf(t, s, contains, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } +func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if assert.NotEmpty(t, object, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { + if assert.NotEmptyf(t, object, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotEqual asserts that the specified values are NOT equal. +// +// assert.NotEqual(t, obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if assert.NotEqual(t, expected, actual, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if assert.NotEqualf(t, expected, actual, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotNil asserts that the specified object is not nil. +// +// assert.NotNil(t, err) +func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if assert.NotNil(t, object, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotNilf asserts that the specified object is not nil. +// +// assert.NotNilf(t, err, "error message %s", "formatted") +func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { + if assert.NotNilf(t, object, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanics(t, func(){ RemainCalm() }) +func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if assert.NotPanics(t, f, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { + if assert.NotPanicsf(t, f, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") +func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if assert.NotRegexp(t, rx, str, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { + if assert.NotRegexpf(t, rx, str, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotSubset asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if assert.NotSubset(t, list, subset, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotSubsetf asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { + if assert.NotSubsetf(t, list, subset, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotZero asserts that i is not the zero value for its type. +func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if assert.NotZero(t, i, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// NotZerof asserts that i is not the zero value for its type. +func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { + if assert.NotZerof(t, i, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panics(t, func(){ GoCrazy() }) +func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if assert.Panics(t, f, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { + if assert.PanicsWithValuef(t, expected, f, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { + if assert.Panicsf(t, f, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Regexp asserts that a specified regexp matches a string. +// +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") +func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if assert.Regexp(t, rx, str, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Regexpf asserts that a specified regexp matches a string. +// +// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { + if assert.Regexpf(t, rx, str, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Subset asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if assert.Subset(t, list, subset, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Subsetf asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { + if assert.Subsetf(t, list, subset, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// True asserts that the specified value is true. +// +// assert.True(t, myBool) +func True(t TestingT, value bool, msgAndArgs ...interface{}) { + if assert.True(t, value, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Truef asserts that the specified value is true. +// +// assert.Truef(t, myBool, "error message %s", "formatted") +func Truef(t TestingT, value bool, msg string, args ...interface{}) { + if assert.Truef(t, value, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { + if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Zero asserts that i is the zero value for its type. +func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if assert.Zero(t, i, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Zerof asserts that i is the zero value for its type. +func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) { + if assert.Zerof(t, i, msg, args...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl new file mode 100644 index 00000000000..6ffc751b5e5 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require.go.tmpl @@ -0,0 +1,6 @@ +{{.Comment}} +func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { + if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } + if h, ok := t.(tHelper); ok { h.Helper() } + t.FailNow() +} diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go new file mode 100644 index 00000000000..9fe41dbdc0c --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -0,0 +1,957 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package require + +import ( + assert "github.com/stretchr/testify/assert" + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Condition(a.t, comp, msgAndArgs...) +} + +// Conditionf uses a Comparison to assert a complex condition. +func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Conditionf(a.t, comp, msg, args...) +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") +func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Contains(a.t, s, contains, msgAndArgs...) +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Containsf(a.t, s, contains, msg, args...) +} + +// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + DirExists(a.t, path, msgAndArgs...) +} + +// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + DirExistsf(a.t, path, msg, args...) +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) +func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ElementsMatchf(a.t, listA, listB, msg, args...) +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Empty(obj) +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Empty(a.t, object, msgAndArgs...) +} + +// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Emptyf(obj, "error message %s", "formatted") +func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Emptyf(a.t, object, msg, args...) +} + +// Equal asserts that two objects are equal. +// +// a.Equal(123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Equal(a.t, expected, actual, msgAndArgs...) +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualError(a.t, theError, errString, msgAndArgs...) +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualErrorf(a.t, theError, errString, msg, args...) +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValues(uint32(123), int32(123)) +func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualValuesf asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) +func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualValuesf(a.t, expected, actual, msg, args...) +} + +// Equalf asserts that two objects are equal. +// +// a.Equalf(123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Equalf(a.t, expected, actual, msg, args...) +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } +func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Error(a.t, err, msgAndArgs...) +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } +func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Errorf(a.t, err, msg, args...) +} + +// Exactly asserts that two objects are equal in value and type. +// +// a.Exactly(int32(123), int64(123)) +func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Exactly(a.t, expected, actual, msgAndArgs...) +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) +func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Exactlyf(a.t, expected, actual, msg, args...) +} + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Fail(a.t, failureMessage, msgAndArgs...) +} + +// FailNow fails test +func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FailNow(a.t, failureMessage, msgAndArgs...) +} + +// FailNowf fails test +func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FailNowf(a.t, failureMessage, msg, args...) +} + +// Failf reports a failure through +func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Failf(a.t, failureMessage, msg, args...) +} + +// False asserts that the specified value is false. +// +// a.False(myBool) +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + False(a.t, value, msgAndArgs...) +} + +// Falsef asserts that the specified value is false. +// +// a.Falsef(myBool, "error message %s", "formatted") +func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Falsef(a.t, value, msg, args...) +} + +// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FileExists(a.t, path, msgAndArgs...) +} + +// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + FileExistsf(a.t, path, msg, args...) +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPError(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPErrorf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPRedirectf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + HTTPSuccessf(a.t, handler, method, url, values, msg, args...) +} + +// Implements asserts that an object is implemented by the specified interface. +// +// a.Implements((*MyInterface)(nil), new(MyObject)) +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Implements(a.t, interfaceObject, object, msgAndArgs...) +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) +func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Implementsf(a.t, interfaceObject, object, msg, args...) +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// a.InDelta(math.Pi, (22 / 7.0), 0.01) +func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaSlicef(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) +func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InDeltaf(a.t, expected, actual, delta, msg, args...) +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + InEpsilonf(a.t, expected, actual, epsilon, msg, args...) +} + +// IsType asserts that the specified objects are of the same type. +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsType(a.t, expectedType, object, msgAndArgs...) +} + +// IsTypef asserts that the specified objects are of the same type. +func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsTypef(a.t, expectedType, object, msg, args...) +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + JSONEq(a.t, expected, actual, msgAndArgs...) +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + JSONEqf(a.t, expected, actual, msg, args...) +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// a.Len(mySlice, 3) +func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Len(a.t, object, length, msgAndArgs...) +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// a.Lenf(mySlice, 3, "error message %s", "formatted") +func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Lenf(a.t, object, length, msg, args...) +} + +// Nil asserts that the specified object is nil. +// +// a.Nil(err) +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Nil(a.t, object, msgAndArgs...) +} + +// Nilf asserts that the specified object is nil. +// +// a.Nilf(err, "error message %s", "formatted") +func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Nilf(a.t, object, msg, args...) +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoError(a.t, err, msgAndArgs...) +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NoErrorf(a.t, err, msg, args...) +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") +func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotContains(a.t, s, contains, msgAndArgs...) +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotContainsf(a.t, s, contains, msg, args...) +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEmpty(a.t, object, msgAndArgs...) +} + +// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEmptyf(a.t, object, msg, args...) +} + +// NotEqual asserts that the specified values are NOT equal. +// +// a.NotEqual(obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqual(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotEqualf(a.t, expected, actual, msg, args...) +} + +// NotNil asserts that the specified object is not nil. +// +// a.NotNil(err) +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotNil(a.t, object, msgAndArgs...) +} + +// NotNilf asserts that the specified object is not nil. +// +// a.NotNilf(err, "error message %s", "formatted") +func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotNilf(a.t, object, msg, args...) +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanics(func(){ RemainCalm() }) +func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotPanics(a.t, f, msgAndArgs...) +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotPanicsf(a.t, f, msg, args...) +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") +func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotRegexp(a.t, rx, str, msgAndArgs...) +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotRegexpf(a.t, rx, str, msg, args...) +} + +// NotSubset asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSubset(a.t, list, subset, msgAndArgs...) +} + +// NotSubsetf asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotSubsetf(a.t, list, subset, msg, args...) +} + +// NotZero asserts that i is not the zero value for its type. +func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotZero(a.t, i, msgAndArgs...) +} + +// NotZerof asserts that i is not the zero value for its type. +func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotZerof(a.t, i, msg, args...) +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panics(func(){ GoCrazy() }) +func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Panics(a.t, f, msgAndArgs...) +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithValue(a.t, expected, f, msgAndArgs...) +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + PanicsWithValuef(a.t, expected, f, msg, args...) +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Panicsf(a.t, f, msg, args...) +} + +// Regexp asserts that a specified regexp matches a string. +// +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") +func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Regexp(a.t, rx, str, msgAndArgs...) +} + +// Regexpf asserts that a specified regexp matches a string. +// +// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Regexpf(a.t, rx, str, msg, args...) +} + +// Subset asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Subset(a.t, list, subset, msgAndArgs...) +} + +// Subsetf asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Subsetf(a.t, list, subset, msg, args...) +} + +// True asserts that the specified value is true. +// +// a.True(myBool) +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + True(a.t, value, msgAndArgs...) +} + +// Truef asserts that the specified value is true. +// +// a.Truef(myBool, "error message %s", "formatted") +func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Truef(a.t, value, msg, args...) +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinDurationf(a.t, expected, actual, delta, msg, args...) +} + +// Zero asserts that i is the zero value for its type. +func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Zero(a.t, i, msgAndArgs...) +} + +// Zerof asserts that i is the zero value for its type. +func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Zerof(a.t, i, msg, args...) +} diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl new file mode 100644 index 00000000000..54124df1d3b --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl @@ -0,0 +1,5 @@ +{{.CommentWithoutT "a"}} +func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { + if h, ok := a.t.(tHelper); ok { h.Helper() } + {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) +} diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go new file mode 100644 index 00000000000..690583a8e03 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/requirements.go @@ -0,0 +1,29 @@ +package require + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) + FailNow() +} + +type tHelper interface { + Helper() +} + +// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful +// for table driven tests. +type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) + +// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful +// for table driven tests. +type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) + +// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful +// for table driven tests. +type BoolAssertionFunc func(TestingT, bool, ...interface{}) + +// ValuesAssertionFunc is a common function prototype when validating an error value. Can be useful +// for table driven tests. +type ErrorAssertionFunc func(TestingT, error, ...interface{}) + +//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl -include-format-funcs diff --git a/vendor/vendor.json b/vendor/vendor.json index b34647bbd03..5221b45453f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1361,6 +1361,12 @@ "version": "v1.2.0", "versionExact": "v1.2.0" }, + { + "checksumSHA1": "wnEANt4k5X/KGwoFyfSSnpxULm4=", + "path": "github.com/stretchr/testify/require", + "revision": "f35b8ab0b5a2cef36673838d662e249dd9c94686", + "revisionTime": "2018-05-06T18:05:49Z" + }, { "checksumSHA1": "CpcG17Q/0k1g2uy8AL26Uu7TouU=", "path": "github.com/theckman/go-flock", From 233dfc064c34772bb1b32adf3f310322b985f87d Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Wed, 11 Jul 2018 21:21:18 -0500 Subject: [PATCH 11/14] Checkpoint --- heartbeat/hbtestutil/hbtestutil.go | 59 +++++ heartbeat/mapscheme/mapscheme.go | 247 -------------------- heartbeat/mapscheme/mapscheme_test.go | 1 - heartbeat/monitors/active/http/http_test.go | 54 ++--- heartbeat/monitors/active/tcp/tcp_test.go | 10 +- heartbeat/valschema/core.go | 167 +++++++++++++ heartbeat/valschema/core_test.go | 129 ++++++++++ heartbeat/valschema/validators.go | 31 +++ 8 files changed, 418 insertions(+), 280 deletions(-) create mode 100644 heartbeat/hbtestutil/hbtestutil.go delete mode 100644 heartbeat/mapscheme/mapscheme.go delete mode 100644 heartbeat/mapscheme/mapscheme_test.go create mode 100644 heartbeat/valschema/core.go create mode 100644 heartbeat/valschema/core_test.go create mode 100644 heartbeat/valschema/validators.go diff --git a/heartbeat/hbtestutil/hbtestutil.go b/heartbeat/hbtestutil/hbtestutil.go new file mode 100644 index 00000000000..f59d01f833e --- /dev/null +++ b/heartbeat/hbtestutil/hbtestutil.go @@ -0,0 +1,59 @@ +package hbtestutil + +import ( + "io" + "net/http" + "net/url" + "strconv" + + "net/http/httptest" + + "github.com/elastic/beats/heartbeat/valschema" +) + +var HelloWorldBody = "hello, world!" + +var HelloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + io.WriteString(w, HelloWorldBody) +}) + +var BadGatewayBody = "Bad Gateway" + +var BadGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadGateway) + io.WriteString(w, BadGatewayBody) +}) + +func ServerPort(server *httptest.Server) (uint16, error) { + u, err := url.Parse(server.URL) + if err != nil { + return 0, err + } + p, err := strconv.Atoi(u.Port()) + if err != nil { + return 0, err + } + return uint16(p), nil +} + +func MonitorChecks(id string, ip string, scheme string, status string) valschema.Map { + return valschema.Map{ + "monitor": valschema.Map{ + "duration.us": valschema.IsDuration, + "id": id, + "ip": ip, + "scheme": scheme, + "status": status, + }, + } +} + +func TcpChecks(port uint16) valschema.Map { + return valschema.Map{ + "tcp": valschema.Map{ + "port": port, + "rtt.connect.us": valschema.IsDuration, + }, + } +} diff --git a/heartbeat/mapscheme/mapscheme.go b/heartbeat/mapscheme/mapscheme.go deleted file mode 100644 index 0470d8a7292..00000000000 --- a/heartbeat/mapscheme/mapscheme.go +++ /dev/null @@ -1,247 +0,0 @@ -package mapscheme - -import ( - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "fmt" - - "io" - "net/http" - - "net/http/httptest" - "net/url" - "strconv" - - "github.com/stretchr/testify/require" - - "github.com/elastic/beats/libbeat/common" -) - -var HelloWorldBody = "hello, world!" - -var HelloWorldHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - io.WriteString(w, HelloWorldBody) -}) - -var BadGatewayBody = "Bad Gateway" - -var BadGatewayHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusBadGateway) - io.WriteString(w, BadGatewayBody) -}) - -var ExactlyEqual = func(expected interface{}) func(t *testing.T, actual interface{}) { - return func(t *testing.T, actual interface{}) { - assert.Equal(t, expected, actual) - } -} - -func ServerPort(server *httptest.Server) (uint16, error) { - u, err := url.Parse(server.URL) - if err != nil { - return 0, err - } - p, err := strconv.Atoi(u.Port()) - if err != nil { - return 0, err - } - return uint16(p), nil -} - -// Functions for testing maps in complex ways - -func MonitorChecks(id string, ip string, scheme string, status string) MapCheckDef { - return MapCheckDef{ - "monitor": MapCheckDef{ - "duration.us": IsDuration, - "id": id, - "ip": ip, - "scheme": scheme, - "status": status, - }, - } -} - -func TcpChecks(port uint16) MapCheckDef { - return MapCheckDef{ - "tcp": MapCheckDef{ - "port": port, - "rtt.connect.us": IsDuration, - }, - } -} - -var IsDuration = func(t *testing.T, _ bool, actual interface{}) { - converted, ok := actual.(time.Duration) - assert.True(t, ok) - assert.True(t, converted >= 0) -} - -var IsNil = func(t *testing.T, _ bool, actual interface{}) { - assert.Nil(t, actual) -} - -var IsString = func(t *testing.T, _ bool, actual interface{}) { - _, ok := actual.(string) - assert.True(t, ok) -} - -type ValueValidator = func(t *testing.T, keyExists bool, actual interface{}) - -type MapCheckDef map[string]interface{} - -type Validator func(*testing.T, common.MapStr) *ValidationResult - -type ValidationResult struct { - expectedFields map[string]struct{} - unexpectedFields map[string]struct{} -} - -func combine(results []*ValidationResult) *ValidationResult { - // Use sets to de-dupe these - output := ValidationResult{} - - for _, res := range results { - for k, _ := range res.expectedFields { - output.expectedFields[k] = struct{}{} - } - } - - for _, res := range results { - for k := range res.unexpectedFields { - // Unexpected fields are those that are in no expectedFields - // for any test - if _, ok := output.expectedFields[k]; ok == false { - output.unexpectedFields[k] = struct{}{} - } - } - } - - return &output -} - -func Compose(validators ...Validator) Validator { - return func(t *testing.T, actual common.MapStr) *ValidationResult { - results := make([]*ValidationResult, len(validators)) - for _, validator := range validators { - result := validator(t, actual) - results = append(results, result) - } - return combine(results) - } -} - -func Strict(validator Validator) Validator { - return func(t *testing.T, actual common.MapStr) *ValidationResult { - res := validator(t, actual) - - return res - } -} - -func Scheme(expected MapCheckDef) Validator { - return func(t *testing.T, actual common.MapStr) *ValidationResult { - return Validate(t, expected, actual) - } -} - -func Validate(t *testing.T, expected MapCheckDef, actual common.MapStr) *ValidationResult { - return validateInternal(t, expected, actual, actual, []string{}) -} - -type WalkObserver func( - key string, - value interface{}, - currentMap common.MapStr, - rootMap common.MapStr, - path []string, - dottedPath string, -) - -func walk(m common.MapStr, wo WalkObserver) { - walkFull(m, m, []string{}, wo) -} - -// TODO: Handle slices/arrays. We intentionally don't handle list types now because we don't need it (yet) -// and it isn't clear in the context of validation what the right thing is to do there beyond letting the user -// perform a custom validation -func walkFull(m common.MapStr, root common.MapStr, path []string, wo WalkObserver) { - for k, v := range m { - wo(k, v, m, root, path, strings.Join(path, ".")) - - // Walk nested maps - if mapV, ok := v.(common.MapStr); ok { - newPath := make([]string, len(path)+1) - copy(newPath, path) - newPath[len(path)] = k // Append the key - walkFull(mapV, root, newPath, wo) - } - } -} - -func walkValidate(t *testing.T, expected MapCheckDef, actual common.MapStr) (output *ValidationResult) { - walk( - common.MapStr(expected), - func(expectedK string, - expectedV interface{}, - currentMap common.MapStr, - rootMap common.MapStr, - path []string, - dottedPath string) { - actualHasKey, err := actual.HasKey(dottedPath) - require.Nil(t, err) - actualV, err := actual.GetValue(dottedPath) - require.Nil(t, err) - - vv, isVV := expectedV.(ValueValidator) - if isVV { - t.Run(fmt.Sprintf("map path|customMatch(%s)", dottedPath), func(t *testing.T) { vv(t, actualHasKey, actualV) }) - } else { // Assert exact equality - t.Run(fmt.Sprintf("map path|%s=>%v", dottedPath, expectedV), func(t *testing.T) { - assert.Equal(t, expectedV, actualV, "Expected %s to equal '%v', but got '%v'", dottedPath, expectedV, actualV) - }) - } - - }) -} - -func validateInternal(t *testing.T, expected MapCheckDef, actual common.MapStr, rootActual common.MapStr, path []string) (output *ValidationResult) { - fmt.Printf("A: %v\n", actual) - - for expectedK, expectedV := range expected { - expectedV := expectedV // Bind locally for subsequent t.Run calls - keyPath := strings.Join(path, ".") + "." + expectedK - mapLeafTest, ok := expectedV.(ValueValidator) - - hasKey, err := actual.HasKey(expectedK) - require.Nil(t, err) - require.True(t, hasKey, fmt.Sprintf("Expectation exists for %s', but the given MapStr does not have it: %v", keyPath, rootActual)) - if hasKey { - output.expectedFields[keyPath] = struct{}{} - } - actualV, _ := actual.GetValue(expectedK) - - if ok { - t.Run(fmt.Sprintf("map path|customMatch(%s)", keyPath), func(t *testing.T) { mapLeafTest(t, actualV) }) - } else if actualVMapStr, nestedOK := actualV.(common.MapStr); nestedOK { - expectedVMapStr, ok := expectedV.(MapCheckDef) - if ok != true { - t.FailNow() - } - assert.True(t, ok) - validateInternal(t, expectedVMapStr, actualVMapStr, rootActual, append(path, expectedK)) - } else { - // assert exact equality otherwise - t.Run(fmt.Sprintf("map path|%s=>%v", keyPath, expectedV), func(t *testing.T) { - assert.Equal(t, expectedV, actualV, "Expected %s to equal '%v', but got '%v'", keyPath, expectedV, actualV) - }) - } - } - - return output -} diff --git a/heartbeat/mapscheme/mapscheme_test.go b/heartbeat/mapscheme/mapscheme_test.go deleted file mode 100644 index f794273fb96..00000000000 --- a/heartbeat/mapscheme/mapscheme_test.go +++ /dev/null @@ -1 +0,0 @@ -package mapscheme diff --git a/heartbeat/monitors/active/http/http_test.go b/heartbeat/monitors/active/http/http_test.go index a22ad44ae70..70e0eae0fc9 100644 --- a/heartbeat/monitors/active/http/http_test.go +++ b/heartbeat/monitors/active/http/http_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/assert" - "github.com/elastic/beats/heartbeat/mapscheme" "github.com/elastic/beats/heartbeat/monitors" + "github.com/elastic/beats/heartbeat/valschema" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" ) @@ -31,53 +31,53 @@ func executeHTTPMonitorHostJob(t *testing.T, handlerFunc http.HandlerFunc) (*htt return server, event } -func httpChecks(urlStr string, statusCode int) mapscheme.MapCheckDef { - return mapscheme.MapCheckDef{ - "http": mapscheme.MapCheckDef{ +func httpChecks(urlStr string, statusCode int) valschema.Map { + return valschema.Map{ + "http": valschema.Map{ "url": urlStr, "response.status_code": statusCode, - "rtt.content.us": mapscheme.IsDuration, - "rtt.response_header.us": mapscheme.IsDuration, - "rtt.total.us": mapscheme.IsDuration, - "rtt.validate.us": mapscheme.IsDuration, - "rtt.write_request.us": mapscheme.IsDuration, + "rtt.content.us": valschema.IsDuration, + "rtt.response_header.us": valschema.IsDuration, + "rtt.total.us": valschema.IsDuration, + "rtt.validate.us": valschema.IsDuration, + "rtt.write_request.us": valschema.IsDuration, }, } } -func httpErrorChecks(urlStr string, statusCode int) mapscheme.MapCheckDef { - return mapscheme.MapCheckDef{ - "error": mapscheme.MapCheckDef{ +func httpErrorChecks(urlStr string, statusCode int) valschema.Map { + return valschema.Map{ + "error": valschema.Map{ "message": "502 Bad Gateway", "type": "validate", }, - "http": mapscheme.MapCheckDef{ + "http": valschema.Map{ "url": urlStr, // TODO: This should work in the future "response.status_code": statusCode, - "rtt.content.us": mapscheme.IsDuration, - "rtt.response_header.us": mapscheme.IsDuration, - "rtt.validate.us": mapscheme.IsDuration, - "rtt.write_request.us": mapscheme.IsDuration, + "rtt.content.us": valschema.IsDuration, + "rtt.response_header.us": valschema.IsDuration, + "rtt.validate.us": valschema.IsDuration, + "rtt.write_request.us": valschema.IsDuration, }, } } func TestOKJob(t *testing.T) { - server, event := executeHTTPMonitorHostJob(t, mapscheme.HelloWorldHandler) - port, err := mapscheme.ServerPort(server) + server, event := executeHTTPMonitorHostJob(t, valschema.HelloWorldHandler) + port, err := valschema.ServerPort(server) assert.Nil(t, err) - mapscheme.Validate(t, mapscheme.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "up"), event.Fields) - mapscheme.Validate(t, mapscheme.TcpChecks(port), event.Fields) - mapscheme.Validate(t, httpChecks(server.URL, http.StatusOK), event.Fields) + valschema.Validate(t, valschema.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "up"), event.Fields) + valschema.Validate(t, valschema.TcpChecks(port), event.Fields) + valschema.Validate(t, httpChecks(server.URL, http.StatusOK), event.Fields) } func TestBadGatewayJob(t *testing.T) { - server, event := executeHTTPMonitorHostJob(t, mapscheme.BadGatewayHandler) - port, err := mapscheme.ServerPort(server) + server, event := executeHTTPMonitorHostJob(t, valschema.BadGatewayHandler) + port, err := valschema.ServerPort(server) assert.Nil(t, err) - mapscheme.Validate(t, mapscheme.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "down"), event.Fields) - mapscheme.Validate(t, mapscheme.TcpChecks(port), event.Fields) - mapscheme.Validate(t, httpErrorChecks(server.URL, http.StatusBadGateway), event.Fields) + valschema.Validate(t, valschema.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "down"), event.Fields) + valschema.Validate(t, valschema.TcpChecks(port), event.Fields) + valschema.Validate(t, httpErrorChecks(server.URL, http.StatusBadGateway), event.Fields) } diff --git a/heartbeat/monitors/active/tcp/tcp_test.go b/heartbeat/monitors/active/tcp/tcp_test.go index 3288359a584..93653c80cea 100644 --- a/heartbeat/monitors/active/tcp/tcp_test.go +++ b/heartbeat/monitors/active/tcp/tcp_test.go @@ -5,16 +5,16 @@ import ( "net/http/httptest" "testing" - "github.com/elastic/beats/heartbeat/mapscheme" "github.com/elastic/beats/heartbeat/monitors" + "github.com/elastic/beats/heartbeat/valschema" "github.com/elastic/beats/libbeat/common" ) func TestUpEndpoint(t *testing.T) { - server := httptest.NewServer(mapscheme.HelloWorldHandler) + server := httptest.NewServer(valschema.HelloWorldHandler) defer server.Close() - port, err := mapscheme.ServerPort(server) + port, err := valschema.ServerPort(server) if err != nil { t.FailNow() } @@ -34,6 +34,6 @@ func TestUpEndpoint(t *testing.T) { t.FailNow() } - mapscheme.Validate(t, mapscheme.MonitorChecks(fmt.Sprintf("tcp-tcp@localhost:%d", port), "127.0.0.1", "tcp", "up"), event.Fields) - mapscheme.Validate(t, mapscheme.TcpChecks(port), event.Fields) + valschema.Validate(t, valschema.MonitorChecks(fmt.Sprintf("tcp-tcp@localhost:%d", port), "127.0.0.1", "tcp", "up"), event.Fields) + valschema.Validate(t, valschema.TcpChecks(port), event.Fields) } diff --git a/heartbeat/valschema/core.go b/heartbeat/valschema/core.go new file mode 100644 index 00000000000..107bb20382e --- /dev/null +++ b/heartbeat/valschema/core.go @@ -0,0 +1,167 @@ +package valschema + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/libbeat/common" +) + +type ValueValidator = func(t *testing.T, keyExists bool, actual interface{}) + +type Map map[string]interface{} + +type StrictMap Map + +type Validator func(*testing.T, common.MapStr) *ValidationResult + +type ValidationResult struct { + valid bool + testedFields map[string]struct{} +} + +func combineResults(results []*ValidationResult) *ValidationResult { + // Use sets to de-dupe these + output := ValidationResult{testedFields: map[string]struct{}{}} + + for _, res := range results { + for k, _ := range res.testedFields { + output.testedFields[k] = struct{}{} + } + } + + return &output +} + +func Compose(validators ...Validator) Validator { + return func(t *testing.T, actual common.MapStr) *ValidationResult { + results := make([]*ValidationResult, len(validators)) + for idx, validator := range validators { + results[idx] = validator(t, actual) + } + return combineResults(results) + } +} + +func Strict(validator Validator) Validator { + return func(t *testing.T, actual common.MapStr) *ValidationResult { + res := validator(t, actual) + + missed := map[string]struct{}{} + + walk(actual, func( + key string, + value interface{}, + currentMap common.MapStr, + rootMap common.MapStr, + path []string, + dottedPath string) { + if _, ok := res.testedFields[dottedPath]; !ok { + missed[dottedPath] = struct{}{} + } + }) + + assert.Empty(t, missed, "Unexpected fields found during strict schema test") + + return res + } +} + +func Schema(expected Map) Validator { + return func(t *testing.T, actual common.MapStr) *ValidationResult { + return Validate(t, expected, actual) + } +} + +func Validate(t *testing.T, expected Map, actual common.MapStr) *ValidationResult { + return walkValidate(t, expected, actual) +} + +type WalkObserver func( + key string, + value interface{}, + currentMap common.MapStr, + rootMap common.MapStr, + path []string, + dottedPath string, +) + +func walk(m common.MapStr, wo WalkObserver) { + walkFull(m, m, []string{}, wo) +} + +// TODO: Handle slices/arrays. We intentionally don't handle list types now because we don't need it (yet) +// and it isn't clear in the context of validation what the right thing is to do there beyond letting the user +// perform a custom validation +func walkFull(m common.MapStr, root common.MapStr, path []string, wo WalkObserver) { + for k, v := range m { + newPath := make([]string, len(path)+1) + copy(newPath, path) + newPath[len(path)] = k // Append the key + + dottedPath := strings.Join(newPath, ".") + + wo(k, v, m, root, newPath, dottedPath) + + // Walk nested maps + vIsMap := false + var mapV common.MapStr + if convertedMS, ok := v.(common.MapStr); ok { + mapV = convertedMS + vIsMap = true + } else if convertedM, ok := v.(Map); ok { + mapV = common.MapStr(convertedM) + vIsMap = true + } + + if vIsMap { + walkFull(mapV, root, newPath, wo) + } + } +} + +func walkValidate(t *testing.T, expected Map, actual common.MapStr) (output *ValidationResult) { + output = &ValidationResult{testedFields: map[string]struct{}{}} + walk( + common.MapStr(expected), + func(expectedK string, + expectedV interface{}, + currentMap common.MapStr, + rootMap common.MapStr, + path []string, + dottedPath string) { + actualHasKey, _ := actual.HasKey(dottedPath) + if actualHasKey { + output.testedFields[dottedPath] = struct{}{} + } + + actualV, _ := actual.GetValue(dottedPath) + + vv, isVV := expectedV.(ValueValidator) + if isVV { + vv(t, actualHasKey, actualV) + } else if sm, isStrictMap := expectedV.(StrictMap); isStrictMap { + // Run a strict validation on this map + if actualM, ok := actualV.(common.MapStr); ok { + Strict(Schema(Map(sm)))(t, actualM) + } else { + assert.Fail(t, "Expected %s to be a strictly defined map, but it was actually '%v'", dottedPath, actualV) + } + } else if _, isMap := expectedV.(Map); !isMap { + assert.Equal(t, expectedV, actualV, "Expected %s to equal '%v', but got '%v'", dottedPath, expectedV, actualV) + } + }) + + return output +} + +func isMapType(v interface{}) bool { + _, isMap := v.(Map) + if isMap { + return isMap + } + _, isStrictMap := v.(StrictMap) + return isStrictMap +} diff --git a/heartbeat/valschema/core_test.go b/heartbeat/valschema/core_test.go new file mode 100644 index 00000000000..06b77036772 --- /dev/null +++ b/heartbeat/valschema/core_test.go @@ -0,0 +1,129 @@ +package valschema + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/libbeat/common" +) + +func TestFlat(t *testing.T) { + m := common.MapStr{ + "foo": "bar", + "baz": 1, + "blargh": time.Duration(100), + } + + validator := Schema(Map{ + "foo": "bar", + "baz": 1, + "blargh": IsDuration, + }) + + validator(t, m) +} + +func TestBadFlat(t *testing.T) { + m := common.MapStr{} + validator := Schema(Map{ + "notafield": IsDuration, + }) + + fakeT := new(testing.T) + assert.Equal(fakeT, "foo", "bar") + + validator(fakeT, m) + assert.True(t, fakeT.Failed()) +} + +func TestNested(t *testing.T) { + m := common.MapStr{ + "foo": common.MapStr{ + "bar": "baz", + "dur": time.Duration(100), + }, + } + + validator := Schema(Map{ + "foo": Map{ + "bar": "baz", + }, + "foo.dur": IsDuration, + }) + + validator(t, m) +} + +func TestStrictFunc(t *testing.T) { + m := common.MapStr{ + "foo": "bar", + "baz": "bot", + } + + validator := Schema(Map{ + "foo": "bar", + }) + + // Should pass, since this is not a strict check + validator(t, m) + + fakeT := new(testing.T) + Strict(validator)(fakeT, m) + assert.True(t, fakeT.Failed()) +} + +func TestComposition(t *testing.T) { + m := common.MapStr{ + "foo": "bar", + "baz": "bot", + } + + fooValidator := Schema(Map{"foo": "bar"}) + bazValidator := Schema(Map{"baz": "bot"}) + composed := Compose(fooValidator, bazValidator) + + fooValidator(t, m) + bazValidator(t, m) + composed(t, m) + + badValidator := Schema(Map{"notakey": "blah"}) + badComposed := Compose(badValidator, composed) + + fakeT := new(testing.T) + badComposed(fakeT, m) + assert.True(t, fakeT.Failed()) +} + +func TestComplex(t *testing.T) { + m := common.MapStr{ + "foo": "bar", + "hash": common.MapStr{ + "baz": 1, + "bot": 2, + "deep_hash": common.MapStr{ + "qux": "quark", + }, + }, + "slice": []string{"pizza", "pasta", "and more"}, + "empty": nil, + } + + validator := Schema(Map{ + "foo": "bar", + "hash": StrictMap{ + "baz": 1, + "bot": 2, + "deep_hash": Map{ + "qux": "quark", + }, + }, + "slice": []string{"pizza", "pasta", "and more"}, + "empty": DoesExist, + "doesNotExist": DoesNotExist, + }) + + validator(t, m) + +} diff --git a/heartbeat/valschema/validators.go b/heartbeat/valschema/validators.go new file mode 100644 index 00000000000..4ca329f7ec7 --- /dev/null +++ b/heartbeat/valschema/validators.go @@ -0,0 +1,31 @@ +package valschema + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var IsDuration = func(t *testing.T, _ bool, actual interface{}) { + converted, ok := actual.(time.Duration) + assert.True(t, ok) + assert.True(t, converted >= 0) +} + +var IsNil = func(t *testing.T, _ bool, actual interface{}) { + assert.Nil(t, actual) +} + +var IsString = func(t *testing.T, _ bool, actual interface{}) { + _, ok := actual.(string) + assert.True(t, ok) +} + +var DoesExist = func(t *testing.T, exists bool, actual interface{}) { + assert.True(t, exists) +} + +var DoesNotExist = func(t *testing.T, exists bool, actual interface{}) { + assert.False(t, exists) +} From db2a00a425546a12ca522a015016cfbb2af2c17c Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Wed, 11 Jul 2018 21:34:04 -0500 Subject: [PATCH 12/14] Checkpoint --- heartbeat/valschema/core.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/heartbeat/valschema/core.go b/heartbeat/valschema/core.go index 107bb20382e..1103868efb1 100644 --- a/heartbeat/valschema/core.go +++ b/heartbeat/valschema/core.go @@ -108,6 +108,9 @@ func walkFull(m common.MapStr, root common.MapStr, path []string, wo WalkObserve // Walk nested maps vIsMap := false var mapV common.MapStr + // Note that we intentionally do not handle StrictMap + // In this branching conditional! That is handled by + // Initializing a whole new validation chain in a lower spot if convertedMS, ok := v.(common.MapStr); ok { mapV = convertedMS vIsMap = true @@ -143,7 +146,6 @@ func walkValidate(t *testing.T, expected Map, actual common.MapStr) (output *Val if isVV { vv(t, actualHasKey, actualV) } else if sm, isStrictMap := expectedV.(StrictMap); isStrictMap { - // Run a strict validation on this map if actualM, ok := actualV.(common.MapStr); ok { Strict(Schema(Map(sm)))(t, actualM) } else { From e7f2792a54652806f3e6fde86df4b31f65f3a7f6 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Wed, 11 Jul 2018 22:16:54 -0500 Subject: [PATCH 13/14] Checkpoint --- heartbeat/monitors/active/http/http_test.go | 63 ++++++++++--------- heartbeat/monitors/active/tcp/tcp_test.go | 18 ++++-- heartbeat/{valschema => skima}/core.go | 24 ++++--- heartbeat/{valschema => skima}/core_test.go | 2 +- heartbeat/skima/validators.go | 31 +++++++++ .../{hbtestutil => testutil}/hbtestutil.go | 24 +++---- heartbeat/valschema/validators.go | 31 --------- 7 files changed, 108 insertions(+), 85 deletions(-) rename heartbeat/{valschema => skima}/core.go (88%) rename heartbeat/{valschema => skima}/core_test.go (99%) create mode 100644 heartbeat/skima/validators.go rename heartbeat/{hbtestutil => testutil}/hbtestutil.go (73%) delete mode 100644 heartbeat/valschema/validators.go diff --git a/heartbeat/monitors/active/http/http_test.go b/heartbeat/monitors/active/http/http_test.go index 70e0eae0fc9..328285cf2df 100644 --- a/heartbeat/monitors/active/http/http_test.go +++ b/heartbeat/monitors/active/http/http_test.go @@ -8,7 +8,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/elastic/beats/heartbeat/monitors" - "github.com/elastic/beats/heartbeat/valschema" + "github.com/elastic/beats/heartbeat/skima" + "github.com/elastic/beats/heartbeat/testutil" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" ) @@ -31,53 +32,57 @@ func executeHTTPMonitorHostJob(t *testing.T, handlerFunc http.HandlerFunc) (*htt return server, event } -func httpChecks(urlStr string, statusCode int) valschema.Map { - return valschema.Map{ - "http": valschema.Map{ +func httpChecks(urlStr string, statusCode int) skima.Validator { + return skima.Schema(skima.Map{ + "http": skima.Map{ "url": urlStr, "response.status_code": statusCode, - "rtt.content.us": valschema.IsDuration, - "rtt.response_header.us": valschema.IsDuration, - "rtt.total.us": valschema.IsDuration, - "rtt.validate.us": valschema.IsDuration, - "rtt.write_request.us": valschema.IsDuration, + "rtt.content.us": skima.IsDuration, + "rtt.response_header.us": skima.IsDuration, + "rtt.total.us": skima.IsDuration, + "rtt.validate.us": skima.IsDuration, + "rtt.write_request.us": skima.IsDuration, }, - } + }) } -func httpErrorChecks(urlStr string, statusCode int) valschema.Map { - return valschema.Map{ - "error": valschema.Map{ +func httpErrorChecks(urlStr string, statusCode int) skima.Validator { + return skima.Schema(skima.Map{ + "error": skima.Map{ "message": "502 Bad Gateway", "type": "validate", }, - "http": valschema.Map{ + "http": skima.Map{ "url": urlStr, // TODO: This should work in the future "response.status_code": statusCode, - "rtt.content.us": valschema.IsDuration, - "rtt.response_header.us": valschema.IsDuration, - "rtt.validate.us": valschema.IsDuration, - "rtt.write_request.us": valschema.IsDuration, + "rtt.content.us": skima.IsDuration, + "rtt.response_header.us": skima.IsDuration, + "rtt.validate.us": skima.IsDuration, + "rtt.write_request.us": skima.IsDuration, }, - } + }) } func TestOKJob(t *testing.T) { - server, event := executeHTTPMonitorHostJob(t, valschema.HelloWorldHandler) - port, err := valschema.ServerPort(server) + server, event := executeHTTPMonitorHostJob(t, testutil.HelloWorldHandler) + port, err := testutil.ServerPort(server) assert.Nil(t, err) - valschema.Validate(t, valschema.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "up"), event.Fields) - valschema.Validate(t, valschema.TcpChecks(port), event.Fields) - valschema.Validate(t, httpChecks(server.URL, http.StatusOK), event.Fields) + skima.Strict(skima.Compose( + testutil.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "up"), + testutil.TcpChecks(port), + httpChecks(server.URL, http.StatusOK), + ))(t, event.Fields) } func TestBadGatewayJob(t *testing.T) { - server, event := executeHTTPMonitorHostJob(t, valschema.BadGatewayHandler) - port, err := valschema.ServerPort(server) + server, event := executeHTTPMonitorHostJob(t, testutil.BadGatewayHandler) + port, err := testutil.ServerPort(server) assert.Nil(t, err) - valschema.Validate(t, valschema.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "down"), event.Fields) - valschema.Validate(t, valschema.TcpChecks(port), event.Fields) - valschema.Validate(t, httpErrorChecks(server.URL, http.StatusBadGateway), event.Fields) + skima.Strict(skima.Compose( + testutil.MonitorChecks("http@"+server.URL, "127.0.0.1", "http", "down"), + testutil.TcpChecks(port), + httpErrorChecks(server.URL, http.StatusBadGateway), + ))(t, event.Fields) } diff --git a/heartbeat/monitors/active/tcp/tcp_test.go b/heartbeat/monitors/active/tcp/tcp_test.go index 93653c80cea..f9f795c92c0 100644 --- a/heartbeat/monitors/active/tcp/tcp_test.go +++ b/heartbeat/monitors/active/tcp/tcp_test.go @@ -6,15 +6,16 @@ import ( "testing" "github.com/elastic/beats/heartbeat/monitors" - "github.com/elastic/beats/heartbeat/valschema" + "github.com/elastic/beats/heartbeat/skima" + "github.com/elastic/beats/heartbeat/testutil" "github.com/elastic/beats/libbeat/common" ) func TestUpEndpoint(t *testing.T) { - server := httptest.NewServer(valschema.HelloWorldHandler) + server := httptest.NewServer(testutil.HelloWorldHandler) defer server.Close() - port, err := valschema.ServerPort(server) + port, err := testutil.ServerPort(server) if err != nil { t.FailNow() } @@ -34,6 +35,13 @@ func TestUpEndpoint(t *testing.T) { t.FailNow() } - valschema.Validate(t, valschema.MonitorChecks(fmt.Sprintf("tcp-tcp@localhost:%d", port), "127.0.0.1", "tcp", "up"), event.Fields) - valschema.Validate(t, valschema.TcpChecks(port), event.Fields) + skima.Strict(skima.Compose( + testutil.MonitorChecks( + fmt.Sprintf("tcp-tcp@localhost:%d", port), + "127.0.0.1", + "tcp", + "up", + ), + testutil.TcpChecks(port), + ))(t, event.Fields) } diff --git a/heartbeat/valschema/core.go b/heartbeat/skima/core.go similarity index 88% rename from heartbeat/valschema/core.go rename to heartbeat/skima/core.go index 1103868efb1..8c1aeee2747 100644 --- a/heartbeat/valschema/core.go +++ b/heartbeat/skima/core.go @@ -1,4 +1,4 @@ -package valschema +package skima import ( "strings" @@ -9,7 +9,7 @@ import ( "github.com/elastic/beats/libbeat/common" ) -type ValueValidator = func(t *testing.T, keyExists bool, actual interface{}) +type ValueValidator = func(t *testing.T, keyExists bool, actual interface{}, dotPath string) type Map map[string]interface{} @@ -97,9 +97,10 @@ func walk(m common.MapStr, wo WalkObserver) { // perform a custom validation func walkFull(m common.MapStr, root common.MapStr, path []string, wo WalkObserver) { for k, v := range m { - newPath := make([]string, len(path)+1) - copy(newPath, path) - newPath[len(path)] = k // Append the key + splitK := strings.Split(k, ".") + var newPath []string + newPath = append(newPath, path...) + newPath = append(newPath, splitK...) dottedPath := strings.Join(newPath, ".") @@ -137,14 +138,23 @@ func walkValidate(t *testing.T, expected Map, actual common.MapStr) (output *Val dottedPath string) { actualHasKey, _ := actual.HasKey(dottedPath) if actualHasKey { - output.testedFields[dottedPath] = struct{}{} + for i := 0; i < len(path); i++ { + p := path[0 : i+1] + subP := strings.Join(p, ".") + output.testedFields[subP] = struct{}{} + } } actualV, _ := actual.GetValue(dottedPath) vv, isVV := expectedV.(ValueValidator) if isVV { - vv(t, actualHasKey, actualV) + // We could wrap this in a t.Run, but that makes + // testing really hard since new(testing.T) won't + // do that. + // TODO: Fix this. + // For now we pass in the dotted path + vv(t, actualHasKey, actualV, dottedPath) } else if sm, isStrictMap := expectedV.(StrictMap); isStrictMap { if actualM, ok := actualV.(common.MapStr); ok { Strict(Schema(Map(sm)))(t, actualM) diff --git a/heartbeat/valschema/core_test.go b/heartbeat/skima/core_test.go similarity index 99% rename from heartbeat/valschema/core_test.go rename to heartbeat/skima/core_test.go index 06b77036772..761bd04a107 100644 --- a/heartbeat/valschema/core_test.go +++ b/heartbeat/skima/core_test.go @@ -1,4 +1,4 @@ -package valschema +package skima import ( "testing" diff --git a/heartbeat/skima/validators.go b/heartbeat/skima/validators.go new file mode 100644 index 00000000000..39b1bc22733 --- /dev/null +++ b/heartbeat/skima/validators.go @@ -0,0 +1,31 @@ +package skima + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var IsDuration = func(t *testing.T, _ bool, actual interface{}, dotPath string) { + converted, ok := actual.(time.Duration) + assert.True(t, ok, dotPath) + assert.True(t, converted >= 0, dotPath) +} + +var IsNil = func(t *testing.T, _ bool, actual interface{}, dotPath string) { + assert.Nil(t, actual, dotPath) +} + +var IsString = func(t *testing.T, _ bool, actual interface{}, dotPath string) { + _, ok := actual.(string) + assert.True(t, ok, dotPath) +} + +var DoesExist = func(t *testing.T, exists bool, actual interface{}, dotPath string) { + assert.True(t, exists, dotPath) +} + +var DoesNotExist = func(t *testing.T, exists bool, actual interface{}, dotPath string) { + assert.False(t, exists, dotPath) +} diff --git a/heartbeat/hbtestutil/hbtestutil.go b/heartbeat/testutil/hbtestutil.go similarity index 73% rename from heartbeat/hbtestutil/hbtestutil.go rename to heartbeat/testutil/hbtestutil.go index f59d01f833e..5654bd94fc8 100644 --- a/heartbeat/hbtestutil/hbtestutil.go +++ b/heartbeat/testutil/hbtestutil.go @@ -1,4 +1,4 @@ -package hbtestutil +package testutil import ( "io" @@ -8,7 +8,7 @@ import ( "net/http/httptest" - "github.com/elastic/beats/heartbeat/valschema" + "github.com/elastic/beats/heartbeat/skima" ) var HelloWorldBody = "hello, world!" @@ -37,23 +37,23 @@ func ServerPort(server *httptest.Server) (uint16, error) { return uint16(p), nil } -func MonitorChecks(id string, ip string, scheme string, status string) valschema.Map { - return valschema.Map{ - "monitor": valschema.Map{ - "duration.us": valschema.IsDuration, +func MonitorChecks(id string, ip string, scheme string, status string) skima.Validator { + return skima.Schema(skima.Map{ + "monitor": skima.Map{ + "duration.us": skima.IsDuration, "id": id, "ip": ip, "scheme": scheme, "status": status, }, - } + }) } -func TcpChecks(port uint16) valschema.Map { - return valschema.Map{ - "tcp": valschema.Map{ +func TcpChecks(port uint16) skima.Validator { + return skima.Schema(skima.Map{ + "tcp": skima.Map{ "port": port, - "rtt.connect.us": valschema.IsDuration, + "rtt.connect.us": skima.IsDuration, }, - } + }) } diff --git a/heartbeat/valschema/validators.go b/heartbeat/valschema/validators.go deleted file mode 100644 index 4ca329f7ec7..00000000000 --- a/heartbeat/valschema/validators.go +++ /dev/null @@ -1,31 +0,0 @@ -package valschema - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var IsDuration = func(t *testing.T, _ bool, actual interface{}) { - converted, ok := actual.(time.Duration) - assert.True(t, ok) - assert.True(t, converted >= 0) -} - -var IsNil = func(t *testing.T, _ bool, actual interface{}) { - assert.Nil(t, actual) -} - -var IsString = func(t *testing.T, _ bool, actual interface{}) { - _, ok := actual.(string) - assert.True(t, ok) -} - -var DoesExist = func(t *testing.T, exists bool, actual interface{}) { - assert.True(t, exists) -} - -var DoesNotExist = func(t *testing.T, exists bool, actual interface{}) { - assert.False(t, exists) -} From c7183a88e8997e79f9b505c3b505215c9176036c Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Thu, 12 Jul 2018 11:56:17 -0500 Subject: [PATCH 14/14] Checkpoint --- heartbeat/skima/core.go | 133 +++++++++++++++++++++++----------- heartbeat/skima/core_test.go | 44 ++++++----- heartbeat/skima/is_defs.go | 62 ++++++++++++++++ heartbeat/skima/validators.go | 31 -------- 4 files changed, 177 insertions(+), 93 deletions(-) create mode 100644 heartbeat/skima/is_defs.go delete mode 100644 heartbeat/skima/validators.go diff --git a/heartbeat/skima/core.go b/heartbeat/skima/core.go index 8c1aeee2747..458212913cb 100644 --- a/heartbeat/skima/core.go +++ b/heartbeat/skima/core.go @@ -2,29 +2,78 @@ package skima import ( "strings" + "testing" "github.com/stretchr/testify/assert" + "fmt" + "github.com/elastic/beats/libbeat/common" ) -type ValueValidator = func(t *testing.T, keyExists bool, actual interface{}, dotPath string) +type ValueResult struct { + valid bool + message string // Reason this is invalid +} + +var ValidValueResult = ValueResult{true, ""} + +type Checker func(v interface{}) ValueResult + +type IsDef struct { + name string + checker Checker +} + +func Is(name string, checker Checker) IsDef { + return IsDef{name, checker} +} + +type ValueValidator = func(isdef IsDef) ValueResult type Map map[string]interface{} type StrictMap Map -type Validator func(*testing.T, common.MapStr) *ValidationResult +type Validator func(common.MapStr) MapResults + +type MapResults map[string][]ValueResult -type ValidationResult struct { - valid bool - testedFields map[string]struct{} +func (r MapResults) RecordResult(path string, result ValueResult) { + if r[path] == nil { + r[path] = []ValueResult{result} + } else { + r[path] = append(r[path], result) + } } -func combineResults(results []*ValidationResult) *ValidationResult { +func (r MapResults) eachResult(f func(string, ValueResult)) { + for path, pathResults := range r { + for _, result := range pathResults { + f(path, result) + } + } +} + +func (r MapResults) Errors() MapResults { + errors := MapResults{} + r.eachResult(func(path string, vr ValueResult) { + if !vr.valid { + errors.RecordResult(path, vr) + } + }) + return errors +} + +func (r MapResults) Valid() bool { + return len(r.Errors()) == 0 +} + +/* +func combineResults(results []*MapResults) *MapResults { // Use sets to de-dupe these - output := ValidationResult{testedFields: map[string]struct{}{}} + output := MapResult{testedFields: map[string]struct{}{}} for _, res := range results { for k, _ := range res.testedFields { @@ -36,18 +85,18 @@ func combineResults(results []*ValidationResult) *ValidationResult { } func Compose(validators ...Validator) Validator { - return func(t *testing.T, actual common.MapStr) *ValidationResult { - results := make([]*ValidationResult, len(validators)) + return func(actual common.MapStr) *MapResult { + results := make([]*MapResult, len(validators)) for idx, validator := range validators { - results[idx] = validator(t, actual) + results[idx] = validator(actual) } return combineResults(results) } } func Strict(validator Validator) Validator { - return func(t *testing.T, actual common.MapStr) *ValidationResult { - res := validator(t, actual) + return func(actual common.MapStr) *MapResult { + res := validator(actual) missed := map[string]struct{}{} @@ -63,20 +112,29 @@ func Strict(validator Validator) Validator { } }) - assert.Empty(t, missed, "Unexpected fields found during strict schema test") + //assert.Empty(t, missed, "Unexpected fields found during strict schema test") return res } } +*/ + +func Test(t *testing.T, r MapResults) { + assert.True(t, r.Valid()) + r.Errors().eachResult(func(p string, vr ValueResult) { + msg := fmt.Sprintf("%s: %s", p, vr.message) + assert.True(t, vr.valid, msg) + }) +} func Schema(expected Map) Validator { - return func(t *testing.T, actual common.MapStr) *ValidationResult { - return Validate(t, expected, actual) + return func(actual common.MapStr) MapResults { + return Validate(expected, actual) } } -func Validate(t *testing.T, expected Map, actual common.MapStr) *ValidationResult { - return walkValidate(t, expected, actual) +func Validate(expected Map, actual common.MapStr) MapResults { + return walkValidate(expected, actual) } type WalkObserver func( @@ -126,8 +184,8 @@ func walkFull(m common.MapStr, root common.MapStr, path []string, wo WalkObserve } } -func walkValidate(t *testing.T, expected Map, actual common.MapStr) (output *ValidationResult) { - output = &ValidationResult{testedFields: map[string]struct{}{}} +func walkValidate(expected Map, actual common.MapStr) (results MapResults) { + results = MapResults{} walk( common.MapStr(expected), func(expectedK string, @@ -136,37 +194,26 @@ func walkValidate(t *testing.T, expected Map, actual common.MapStr) (output *Val rootMap common.MapStr, path []string, dottedPath string) { - actualHasKey, _ := actual.HasKey(dottedPath) - if actualHasKey { - for i := 0; i < len(path); i++ { - p := path[0 : i+1] - subP := strings.Join(p, ".") - output.testedFields[subP] = struct{}{} - } - } actualV, _ := actual.GetValue(dottedPath) - vv, isVV := expectedV.(ValueValidator) - if isVV { - // We could wrap this in a t.Run, but that makes - // testing really hard since new(testing.T) won't - // do that. - // TODO: Fix this. - // For now we pass in the dotted path - vv(t, actualHasKey, actualV, dottedPath) - } else if sm, isStrictMap := expectedV.(StrictMap); isStrictMap { - if actualM, ok := actualV.(common.MapStr); ok { - Strict(Schema(Map(sm)))(t, actualM) - } else { - assert.Fail(t, "Expected %s to be a strictly defined map, but it was actually '%v'", dottedPath, actualV) - } + /*else if sm, isStrictMap := expectedV.(StrictMap); isStrictMap { + if actualM, ok := actualV.(common.MapStr); ok { + Strict(Schema(Map(sm)))(actualM) + } else { + //assert.Fail(t, "Expected %s to be a strictly defined map, but it was actually '%v'", dottedPath, actualV) + } + } */ + + isDef, isIsDef := expectedV.(IsDef) + if isIsDef { + results.RecordResult(dottedPath, isDef.checker(actualV)) } else if _, isMap := expectedV.(Map); !isMap { - assert.Equal(t, expectedV, actualV, "Expected %s to equal '%v', but got '%v'", dottedPath, expectedV, actualV) + results.RecordResult(dottedPath, IsEqual(expectedV).checker(actualV)) } }) - return output + return results } func isMapType(v interface{}) bool { diff --git a/heartbeat/skima/core_test.go b/heartbeat/skima/core_test.go index 761bd04a107..9e6b91d4505 100644 --- a/heartbeat/skima/core_test.go +++ b/heartbeat/skima/core_test.go @@ -2,40 +2,43 @@ package skima import ( "testing" - "time" "github.com/stretchr/testify/assert" + "time" + "github.com/elastic/beats/libbeat/common" ) func TestFlat(t *testing.T) { m := common.MapStr{ - "foo": "bar", - "baz": 1, - "blargh": time.Duration(100), + "foo": "bar", + "baz": 1, } - validator := Schema(Map{ - "foo": "bar", - "baz": 1, - "blargh": IsDuration, - }) + results := Schema(Map{ + "foo": "bar", + "baz": IsIntGt(0), + })(m) - validator(t, m) + Test(t, results) } func TestBadFlat(t *testing.T) { m := common.MapStr{} - validator := Schema(Map{ - "notafield": IsDuration, - }) fakeT := new(testing.T) - assert.Equal(fakeT, "foo", "bar") - validator(fakeT, m) + results := Schema(Map{ + "notafield": IsDuration, + })(m) + + Test(fakeT, results) assert.True(t, fakeT.Failed()) + + result := results["notafield"][0] + assert.False(t, result.valid) + assert.Contains(t, result.message, "Expected a time.duration") } func TestNested(t *testing.T) { @@ -46,16 +49,19 @@ func TestNested(t *testing.T) { }, } - validator := Schema(Map{ + results := Schema(Map{ "foo": Map{ "bar": "baz", }, "foo.dur": IsDuration, - }) + })(m) - validator(t, m) + Test(t, results) + + assert.Len(t, results, 3, "One result per matcher") } +/* func TestStrictFunc(t *testing.T) { m := common.MapStr{ "foo": "bar", @@ -126,4 +132,4 @@ func TestComplex(t *testing.T) { validator(t, m) -} +}*/ diff --git a/heartbeat/skima/is_defs.go b/heartbeat/skima/is_defs.go new file mode 100644 index 00000000000..1b50de0573f --- /dev/null +++ b/heartbeat/skima/is_defs.go @@ -0,0 +1,62 @@ +package skima + +import ( + "time" + + "fmt" +) + +var IsDuration = Is("is a duration", func(v interface{}) ValueResult { + if _, ok := v.(time.Duration); ok { + return ValidValueResult + } + return ValueResult{ + false, + fmt.Sprintf("Expected a time.duration, got '%v' which is a %T", v, v), + } +}) + +func IsEqual(to interface{}) IsDef { + return Is("equals", func(v interface{}) ValueResult { + if v == to { + return ValidValueResult + } + return ValueResult{ + false, + fmt.Sprintf("%v != %v", v, to), + } + }) +} + +var IsNil = Is("is nil", func(v interface{}) ValueResult { + if v == nil { + return ValidValueResult + } + return ValueResult{ + false, + fmt.Sprint("Value %v is not nil", v), + } +}) + +func intGtChecker(than int) Checker { + return func(v interface{}) ValueResult { + n, ok := v.(int) + if !ok { + msg := fmt.Sprintf("%v is a %T, but was expecting an int!", v, v) + return ValueResult{false, msg} + } + + if n > than { + return ValidValueResult + } + + return ValueResult{ + false, + fmt.Sprintf("%v is not greater than %v", n, than), + } + } +} + +func IsIntGt(than int) IsDef { + return Is("greater than", intGtChecker(than)) +} diff --git a/heartbeat/skima/validators.go b/heartbeat/skima/validators.go deleted file mode 100644 index 39b1bc22733..00000000000 --- a/heartbeat/skima/validators.go +++ /dev/null @@ -1,31 +0,0 @@ -package skima - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var IsDuration = func(t *testing.T, _ bool, actual interface{}, dotPath string) { - converted, ok := actual.(time.Duration) - assert.True(t, ok, dotPath) - assert.True(t, converted >= 0, dotPath) -} - -var IsNil = func(t *testing.T, _ bool, actual interface{}, dotPath string) { - assert.Nil(t, actual, dotPath) -} - -var IsString = func(t *testing.T, _ bool, actual interface{}, dotPath string) { - _, ok := actual.(string) - assert.True(t, ok, dotPath) -} - -var DoesExist = func(t *testing.T, exists bool, actual interface{}, dotPath string) { - assert.True(t, exists, dotPath) -} - -var DoesNotExist = func(t *testing.T, exists bool, actual interface{}, dotPath string) { - assert.False(t, exists, dotPath) -}