mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
add more/refacotr current test cases of job service
Signed-off-by: Steven Zou <szou@vmware.com>
This commit is contained in:
parent
6f8a80d21c
commit
fae5d1304d
16
src/Gopkg.lock
generated
16
src/Gopkg.lock
generated
@ -440,16 +440,25 @@
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:994df93785d966f82180e17a0857fa53f7155cddca3898ad00b27e8d4481e4ae"
|
||||
digest = "1:ac83cf90d08b63ad5f7e020ef480d319ae890c208f8524622a2f3136e2686b02"
|
||||
name = "github.com/stretchr/objx"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:288e2ba4192b77ec619875ab54d82e2179ca8978e8baa690dcb4343a4a1f4da7"
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"mock",
|
||||
"require",
|
||||
"suite",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b91bfb9ebec76498946beb6af7c0230c7cc7ba6c"
|
||||
version = "v1.2.0"
|
||||
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ab3259b9f5008a18ff8c1cc34623eccce354f3a9faf5b409983cd6717d64b40b"
|
||||
@ -749,6 +758,7 @@
|
||||
"github.com/pkg/errors",
|
||||
"github.com/robfig/cron",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/mock",
|
||||
"github.com/stretchr/testify/require",
|
||||
"github.com/stretchr/testify/suite",
|
||||
"golang.org/x/crypto/pbkdf2",
|
||||
|
@ -52,7 +52,7 @@ ignored = ["github.com/goharbor/harbor/tests*"]
|
||||
name = "github.com/go-sql-driver/mysql"
|
||||
version = "=1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
[[override]]
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
version = "=1.6.0"
|
||||
|
||||
@ -66,7 +66,7 @@ ignored = ["github.com/goharbor/harbor/tests*"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "=1.2.0"
|
||||
version = "=1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/handlers"
|
||||
|
@ -18,329 +18,309 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/query"
|
||||
"github.com/goharbor/harbor/src/jobservice/errs"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/worker"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/models"
|
||||
)
|
||||
|
||||
const fakeSecret = "I'mfakesecret"
|
||||
const (
|
||||
secretKey = "CORE_SECRET"
|
||||
fakeSecret = "I'mfakesecret"
|
||||
)
|
||||
|
||||
var testingAuthProvider = &SecretAuthenticator{}
|
||||
var testingHandler = NewDefaultHandler(&fakeController{})
|
||||
var testingRouter = NewBaseRouter(testingHandler, testingAuthProvider)
|
||||
var client = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 20,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
},
|
||||
// APIHandlerTestSuite tests functions of API handler
|
||||
type APIHandlerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
server *Server
|
||||
controller *fakeController
|
||||
APIAddr string
|
||||
client *http.Client
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func TestUnAuthorizedAccess(t *testing.T) {
|
||||
exportUISecret("hello")
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *APIHandlerTestSuite) SetupSuite() {
|
||||
os.Setenv(secretKey, fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
res, err := getReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job", port))
|
||||
if e := expectFormatedError(res, err); e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
if strings.Index(err.Error(), "401") == -1 {
|
||||
t.Fatalf("expect '401' but got none 401 error")
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestLaunchJobFailed(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
resData, err := postReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs", port), createJobReq(false))
|
||||
if e := expectFormatedError(resData, err); e != nil {
|
||||
t.Error(e)
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestLaunchJobSucceed(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
res, err := postReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs", port), createJobReq(true))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
obj, err := getResult(res)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.Stats.JobID != "fake_ID_ok" {
|
||||
t.Fatalf("expect job ID 'fake_ID_ok' but got '%s'\n", obj.Stats.JobID)
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestGetJobFailed(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
res, err := getReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job", port))
|
||||
if e := expectFormatedError(res, err); e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestGetJobSucceed(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
res, err := getReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job_ok", port))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
obj, err := getResult(res)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if obj.Stats.JobName != "testing" || obj.Stats.JobID != "fake_ID_ok" {
|
||||
t.Fatalf("expect job ID 'fake_ID_ok' of 'testing', but got '%s'\n", obj.Stats.JobID)
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestJobActionFailed(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
actionReq, err := createJobActionReq("stop")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resData, err := postReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job", port), actionReq)
|
||||
expectFormatedError(resData, err)
|
||||
|
||||
actionReq, err = createJobActionReq("cancel")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resData, err = postReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job", port), actionReq)
|
||||
expectFormatedError(resData, err)
|
||||
|
||||
actionReq, err = createJobActionReq("retry")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resData, err = postReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job", port), actionReq)
|
||||
expectFormatedError(resData, err)
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestJobActionSucceed(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
actionReq, err := createJobActionReq("stop")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = postReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job_ok", port), actionReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actionReq, err = createJobActionReq("cancel")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = postReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job_ok", port), actionReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actionReq, err = createJobActionReq("retry")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = postReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job_ok", port), actionReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestCheckStatus(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
resData, err := getReq(fmt.Sprintf("http://localhost:%d/api/v1/stats", port))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
poolStats := &models.JobPoolStats{
|
||||
Pools: make([]*models.JobPoolStatsData, 0),
|
||||
}
|
||||
err = json.Unmarshal(resData, poolStats)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if poolStats.Pools[0].WorkerPoolID != "fake_pool_ID" {
|
||||
t.Fatalf("expect worker ID 'fake_pool_ID' but got '%s'", poolStats.Pools[0].WorkerPoolID)
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestGetJobLogInvalidID(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
_, err := getReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/%%2F..%%2Fpasswd/log", port))
|
||||
if err == nil || strings.Contains(err.Error(), "400") {
|
||||
t.Fatalf("Expected 400 error but got: %v", err)
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestGetJobLog(t *testing.T) {
|
||||
exportUISecret(fakeSecret)
|
||||
|
||||
server, port, ctx := createServer()
|
||||
server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
|
||||
resData, err := getReq(fmt.Sprintf("http://localhost:%d/api/v1/jobs/fake_job_ok/log", port))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(resData) == 0 {
|
||||
t.Fatal("expect job log but got nothing")
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
ctx.WG.Wait()
|
||||
}
|
||||
|
||||
func expectFormatedError(data []byte, err error) error {
|
||||
if err == nil {
|
||||
return errors.New("expect error but got nil")
|
||||
}
|
||||
|
||||
if err != nil && len(data) <= 0 {
|
||||
return errors.New("expect error but got nothing")
|
||||
}
|
||||
|
||||
if err != nil && len(data) > 0 {
|
||||
var m = make(map[string]interface{})
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := m["code"]; !ok {
|
||||
return errors.New("malformated error")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createJobReq(ok bool) []byte {
|
||||
params := make(map[string]interface{})
|
||||
params["image"] = "testing:v1"
|
||||
name := "fake_job_ok"
|
||||
if !ok {
|
||||
name = "fake_job_error"
|
||||
}
|
||||
req := &models.JobRequest{
|
||||
Job: &models.JobData{
|
||||
Name: name,
|
||||
Parameters: params,
|
||||
Metadata: &models.JobMetadata{
|
||||
JobKind: "Periodic",
|
||||
Cron: "5 * * * * *",
|
||||
IsUnique: true,
|
||||
},
|
||||
StatusHook: "http://localhost:39999",
|
||||
suite.client = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 20,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(req)
|
||||
return data
|
||||
suite.createServer()
|
||||
|
||||
go suite.server.Start()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
func createJobActionReq(action string) ([]byte, error) {
|
||||
actionReq := models.JobActionRequest{
|
||||
Action: action,
|
||||
// TearDownSuite clears test suite
|
||||
func (suite *APIHandlerTestSuite) TearDownSuite() {
|
||||
os.Unsetenv(secretKey)
|
||||
suite.server.Stop()
|
||||
suite.cancel()
|
||||
}
|
||||
|
||||
// TestAPIHandlerTestSuite is suite entry for 'go test'
|
||||
func TestAPIHandlerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(APIHandlerTestSuite))
|
||||
}
|
||||
|
||||
// TestUnAuthorizedAccess ...
|
||||
func (suite *APIHandlerTestSuite) TestUnAuthorizedAccess() {
|
||||
os.Unsetenv(secretKey)
|
||||
defer func() {
|
||||
os.Setenv(secretKey, fakeSecret)
|
||||
}()
|
||||
|
||||
_, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job"))
|
||||
assert.Equal(suite.T(), 401, code, "expect '401' but got none 401 error")
|
||||
}
|
||||
|
||||
// TestLaunchJobFailed ...
|
||||
func (suite *APIHandlerTestSuite) TestLaunchJobFailed() {
|
||||
req := createJobReq()
|
||||
bytes, _ := json.Marshal(req)
|
||||
|
||||
fc1 := &fakeController{}
|
||||
fc1.On("LaunchJob", req).Return(nil, errs.BadRequestError(req.Job.Name))
|
||||
suite.controller = fc1
|
||||
_, code := suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs"), bytes)
|
||||
assert.Equal(suite.T(), 400, code, "expect 400 bad request but got %d", code)
|
||||
|
||||
fc2 := &fakeController{}
|
||||
fc2.On("LaunchJob", req).Return(nil, errs.ConflictError(req.Job.Name))
|
||||
suite.controller = fc2
|
||||
_, code = suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs"), bytes)
|
||||
assert.Equal(suite.T(), 409, code, "expect 409 conflict but got %d", code)
|
||||
|
||||
fc3 := &fakeController{}
|
||||
fc3.On("LaunchJob", req).Return(nil, errs.LaunchJobError(errors.New("testing launch job")))
|
||||
suite.controller = fc3
|
||||
_, code = suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs"), bytes)
|
||||
assert.Equal(suite.T(), 500, code, "expect 500 internal server error but got %d", code)
|
||||
}
|
||||
|
||||
// TestLaunchJobSucceed ...
|
||||
func (suite *APIHandlerTestSuite) TestLaunchJobSucceed() {
|
||||
req := createJobReq()
|
||||
bytes, _ := json.Marshal(req)
|
||||
|
||||
fc := &fakeController{}
|
||||
fc.On("LaunchJob", req).Return(createJobStats("sample", "Generic", ""), nil)
|
||||
suite.controller = fc
|
||||
|
||||
_, code := suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs"), bytes)
|
||||
assert.Equal(suite.T(), 202, code, "expected 202 created but got %d when launching job", code)
|
||||
}
|
||||
|
||||
// TestGetJobFailed ...
|
||||
func (suite *APIHandlerTestSuite) TestGetJobFailed() {
|
||||
fc := &fakeController{}
|
||||
fc.On("GetJob", "fake_job_ID").Return(nil, errs.NoObjectFoundError("fake_job_ID"))
|
||||
suite.controller = fc
|
||||
|
||||
_, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID"))
|
||||
assert.Equal(suite.T(), 404, code, "expected 404 not found but got %d when getting job", code)
|
||||
}
|
||||
|
||||
// TestGetJobSucceed ...
|
||||
func (suite *APIHandlerTestSuite) TestGetJobSucceed() {
|
||||
fc := &fakeController{}
|
||||
fc.On("GetJob", "fake_job_ID").Return(createJobStats("sample", "Generic", ""), nil)
|
||||
suite.controller = fc
|
||||
|
||||
res, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID"))
|
||||
require.Equal(suite.T(), 200, code, "expected 200 ok but got %d when getting job", code)
|
||||
stats, err := getResult(res)
|
||||
require.Nil(suite.T(), err, "no error should be occurred when unmarshal job stats")
|
||||
assert.Equal(suite.T(), "fake_job_ID", stats.Info.JobID, "expected job ID 'fake_job_ID' but got %s", stats.Info.JobID)
|
||||
}
|
||||
|
||||
// TestJobActionFailed ...
|
||||
func (suite *APIHandlerTestSuite) TestJobActionFailed() {
|
||||
actionReq := createJobActionReq("not-support")
|
||||
data, _ := json.Marshal(actionReq)
|
||||
_, code := suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID"), data)
|
||||
assert.Equal(suite.T(), 501, code, "expected 501 not implemented but got %d", code)
|
||||
|
||||
fc1 := &fakeController{}
|
||||
fc1.On("StopJob", "fake_job_ID_not").Return(errs.NoObjectFoundError("fake_job_ID_not"))
|
||||
suite.controller = fc1
|
||||
actionReq = createJobActionReq("stop")
|
||||
data, _ = json.Marshal(actionReq)
|
||||
_, code = suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID_not"), data)
|
||||
assert.Equal(suite.T(), 404, code, "expected 404 not found but got %d", code)
|
||||
|
||||
fc2 := &fakeController{}
|
||||
fc2.On("StopJob", "fake_job_ID").Return(errs.BadRequestError("fake_job_ID"))
|
||||
suite.controller = fc2
|
||||
_, code = suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID"), data)
|
||||
assert.Equal(suite.T(), 400, code, "expected 400 bad request but got %d", code)
|
||||
|
||||
fc3 := &fakeController{}
|
||||
fc3.On("StopJob", "fake_job_ID").Return(errs.StopJobError(errors.New("testing error")))
|
||||
suite.controller = fc3
|
||||
_, code = suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID"), data)
|
||||
assert.Equal(suite.T(), 500, code, "expected 500 internal server but got %d", code)
|
||||
}
|
||||
|
||||
// TestJobActionSucceed ...
|
||||
func (suite *APIHandlerTestSuite) TestJobActionSucceed() {
|
||||
fc := &fakeController{}
|
||||
fc.On("StopJob", "fake_job_ID_not").Return(nil)
|
||||
suite.controller = fc
|
||||
actionReq := createJobActionReq("stop")
|
||||
data, _ := json.Marshal(actionReq)
|
||||
_, code := suite.postReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID_not"), data)
|
||||
assert.Equal(suite.T(), 204, code, "expected 204 no content but got %d", code)
|
||||
}
|
||||
|
||||
// TestCheckStatus ...
|
||||
func (suite *APIHandlerTestSuite) TestCheckStatus() {
|
||||
statsRes := &worker.Stats{
|
||||
Pools: []*worker.StatsData{
|
||||
{
|
||||
WorkerPoolID: "my-worker-pool-ID",
|
||||
},
|
||||
},
|
||||
}
|
||||
fc := &fakeController{}
|
||||
fc.On("CheckStatus").Return(statsRes, nil)
|
||||
suite.controller = fc
|
||||
|
||||
bytes, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "stats"))
|
||||
require.Equal(suite.T(), 200, code, "expected 200 ok when getting worker stats but got %d", code)
|
||||
|
||||
poolStats := &worker.Stats{
|
||||
Pools: make([]*worker.StatsData, 0),
|
||||
}
|
||||
err := json.Unmarshal(bytes, poolStats)
|
||||
assert.Nil(suite.T(), err, "no error should be occurred when unmarshal worker stats")
|
||||
assert.Equal(suite.T(), 1, len(poolStats.Pools), "at least 1 pool exists but got %d", len(poolStats.Pools))
|
||||
assert.Equal(suite.T(), "my-worker-pool-ID", poolStats.Pools[0].WorkerPoolID, "expected pool ID 'my-worker-pool-ID' but got %s", poolStats.Pools[0].WorkerPoolID)
|
||||
}
|
||||
|
||||
// TestGetJobLogInvalidID ...
|
||||
func (suite *APIHandlerTestSuite) TestGetJobLogInvalidID() {
|
||||
fc := &fakeController{}
|
||||
fc.On("GetJobLogData", "fake_job_ID_not").Return(nil, errs.NoObjectFoundError("fake_job_ID_not"))
|
||||
suite.controller = fc
|
||||
|
||||
_, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID_not/log"))
|
||||
assert.Equal(suite.T(), 404, code, "expected 404 not found but got %d", code)
|
||||
}
|
||||
|
||||
// TestGetJobLog ...
|
||||
func (suite *APIHandlerTestSuite) TestGetJobLog() {
|
||||
fc := &fakeController{}
|
||||
fc.On("GetJobLogData", "fake_job_ID").Return([]byte("hello log"), nil)
|
||||
suite.controller = fc
|
||||
|
||||
resData, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID/log"))
|
||||
require.Equal(suite.T(), 200, code, "expected 200 ok but got %d", code)
|
||||
assert.Equal(suite.T(), "hello log", string(resData))
|
||||
}
|
||||
|
||||
// TestGetPeriodicExecutionsWithoutQuery ...
|
||||
func (suite *APIHandlerTestSuite) TestGetPeriodicExecutionsWithoutQuery() {
|
||||
q := &query.Parameter{
|
||||
PageNumber: 1,
|
||||
PageSize: query.DefaultPageSize,
|
||||
Extras: make(query.ExtraParameters),
|
||||
}
|
||||
|
||||
return json.Marshal(&actionReq)
|
||||
fc := &fakeController{}
|
||||
fc.On("GetPeriodicExecutions", "fake_job_ID", q).
|
||||
Return([]*job.Stats{createJobStats("sample", "Generic", "")}, int64(1), nil)
|
||||
suite.controller = fc
|
||||
|
||||
_, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID/executions"))
|
||||
assert.Equal(suite.T(), 200, code, "expected 200 ok but got %d", code)
|
||||
}
|
||||
|
||||
func postReq(url string, data []byte) ([]byte, error) {
|
||||
// TestGetPeriodicExecutionsWithQuery ...
|
||||
func (suite *APIHandlerTestSuite) TestGetPeriodicExecutionsWithQuery() {
|
||||
extras := make(query.ExtraParameters)
|
||||
extras.Set(query.ExtraParamKeyNonStoppedOnly, true)
|
||||
q := &query.Parameter{
|
||||
PageNumber: 2,
|
||||
PageSize: 50,
|
||||
Extras: extras,
|
||||
}
|
||||
|
||||
fc := &fakeController{}
|
||||
fc.On("GetPeriodicExecutions", "fake_job_ID", q).
|
||||
Return([]*job.Stats{createJobStats("sample", "Generic", "")}, int64(1), nil)
|
||||
suite.controller = fc
|
||||
|
||||
_, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/fake_job_ID/executions?page_number=2&page_size=50&non_dead_only=true"))
|
||||
assert.Equal(suite.T(), 200, code, "expected 200 ok but got %d", code)
|
||||
}
|
||||
|
||||
// TestScheduledJobs ...
|
||||
func (suite *APIHandlerTestSuite) TestScheduledJobs() {
|
||||
q := &query.Parameter{
|
||||
PageNumber: 2,
|
||||
PageSize: 50,
|
||||
Extras: make(query.ExtraParameters),
|
||||
}
|
||||
|
||||
fc := &fakeController{}
|
||||
fc.On("ScheduledJobs", q).
|
||||
Return([]*job.Stats{createJobStats("sample", "Generic", "")}, int64(1), nil)
|
||||
suite.controller = fc
|
||||
|
||||
_, code := suite.getReq(fmt.Sprintf("%s/%s", suite.APIAddr, "jobs/scheduled?page_number=2&page_size=50"))
|
||||
assert.Equal(suite.T(), 200, code, "expected 200 ok but got %d", code)
|
||||
}
|
||||
|
||||
// createServer ...
|
||||
func (suite *APIHandlerTestSuite) createServer() {
|
||||
port := uint(30000 + rand.Intn(1000))
|
||||
suite.APIAddr = fmt.Sprintf("http://localhost:%d/api/v1", port)
|
||||
|
||||
config := ServerConfig{
|
||||
Protocol: "http",
|
||||
Port: port,
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
testingRouter := NewBaseRouter(
|
||||
NewDefaultHandler(suite),
|
||||
&SecretAuthenticator{},
|
||||
)
|
||||
suite.server = NewServer(ctx, testingRouter, config)
|
||||
suite.cancel = cancel
|
||||
}
|
||||
|
||||
// postReq ...
|
||||
func (suite *APIHandlerTestSuite) postReq(url string, data []byte) ([]byte, int) {
|
||||
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(string(data)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
req.Header.Set(authHeader, fmt.Sprintf("%s %s", secretPrefix, fakeSecret))
|
||||
|
||||
res, err := client.Do(req)
|
||||
res, err := suite.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
var (
|
||||
@ -351,144 +331,185 @@ func postReq(url string, data []byte) ([]byte, error) {
|
||||
if res.ContentLength > 0 {
|
||||
resData, err = ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0
|
||||
}
|
||||
}
|
||||
|
||||
if res.StatusCode >= http.StatusOK && res.StatusCode <= http.StatusNoContent {
|
||||
return resData, nil
|
||||
}
|
||||
|
||||
return resData, fmt.Errorf("expect status code '200,201,202,204', but got '%d'", res.StatusCode)
|
||||
return resData, res.StatusCode
|
||||
}
|
||||
|
||||
func getReq(url string) ([]byte, error) {
|
||||
// getReq ...
|
||||
func (suite *APIHandlerTestSuite) getReq(url string) ([]byte, int) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
req.Header.Set(authHeader, fmt.Sprintf("%s %s", secretPrefix, fakeSecret))
|
||||
|
||||
res, err := client.Do(req)
|
||||
res, err := suite.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return data, fmt.Errorf("expect status code '200', but got '%d'", res.StatusCode)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
return data, res.StatusCode
|
||||
}
|
||||
|
||||
func exportUISecret(secret string) {
|
||||
os.Setenv("CORE_SECRET", secret)
|
||||
func (suite *APIHandlerTestSuite) LaunchJob(req *job.Request) (*job.Stats, error) {
|
||||
return suite.controller.LaunchJob(req)
|
||||
}
|
||||
|
||||
type fakeController struct{}
|
||||
|
||||
func (fc *fakeController) LaunchJob(req models.JobRequest) (models.JobStats, error) {
|
||||
if req.Job.Name != "fake_job_ok" || req.Job.Metadata == nil {
|
||||
return models.JobStats{}, errors.New("failed")
|
||||
}
|
||||
|
||||
return createJobStats(req.Job.Name, req.Job.Metadata.JobKind, req.Job.Metadata.Cron), nil
|
||||
func (suite *APIHandlerTestSuite) GetJob(jobID string) (*job.Stats, error) {
|
||||
return suite.controller.GetJob(jobID)
|
||||
}
|
||||
|
||||
func (fc *fakeController) GetJob(jobID string) (models.JobStats, error) {
|
||||
if jobID != "fake_job_ok" {
|
||||
return models.JobStats{}, errors.New("failed")
|
||||
func (suite *APIHandlerTestSuite) StopJob(jobID string) error {
|
||||
return suite.controller.StopJob(jobID)
|
||||
}
|
||||
|
||||
func (suite *APIHandlerTestSuite) RetryJob(jobID string) error {
|
||||
return suite.controller.RetryJob(jobID)
|
||||
}
|
||||
|
||||
func (suite *APIHandlerTestSuite) CheckStatus() (*worker.Stats, error) {
|
||||
return suite.controller.CheckStatus()
|
||||
}
|
||||
|
||||
func (suite *APIHandlerTestSuite) GetJobLogData(jobID string) ([]byte, error) {
|
||||
return suite.controller.GetJobLogData(jobID)
|
||||
}
|
||||
|
||||
func (suite *APIHandlerTestSuite) GetPeriodicExecutions(periodicJobID string, query *query.Parameter) ([]*job.Stats, int64, error) {
|
||||
return suite.controller.GetPeriodicExecutions(periodicJobID, query)
|
||||
}
|
||||
|
||||
func (suite *APIHandlerTestSuite) ScheduledJobs(query *query.Parameter) ([]*job.Stats, int64, error) {
|
||||
return suite.controller.ScheduledJobs(query)
|
||||
}
|
||||
|
||||
type fakeController struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (fc *fakeController) LaunchJob(req *job.Request) (*job.Stats, error) {
|
||||
args := fc.Called(req)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return createJobStats("testing", "Generic", ""), nil
|
||||
return args.Get(0).(*job.Stats), nil
|
||||
}
|
||||
|
||||
func (fc *fakeController) GetJob(jobID string) (*job.Stats, error) {
|
||||
args := fc.Called(jobID)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return args.Get(0).(*job.Stats), nil
|
||||
}
|
||||
|
||||
func (fc *fakeController) StopJob(jobID string) error {
|
||||
if jobID == "fake_job_ok" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("failed")
|
||||
args := fc.Called(jobID)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (fc *fakeController) RetryJob(jobID string) error {
|
||||
if jobID == "fake_job_ok" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("failed")
|
||||
args := fc.Called(jobID)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (fc *fakeController) CancelJob(jobID string) error {
|
||||
if jobID == "fake_job_ok" {
|
||||
return nil
|
||||
func (fc *fakeController) CheckStatus() (*worker.Stats, error) {
|
||||
args := fc.Called()
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return errors.New("failed")
|
||||
}
|
||||
|
||||
func (fc *fakeController) CheckStatus() (models.JobPoolStats, error) {
|
||||
return models.JobPoolStats{
|
||||
Pools: []*models.JobPoolStatsData{{
|
||||
WorkerPoolID: "fake_pool_ID",
|
||||
Status: "running",
|
||||
StartedAt: time.Now().Unix(),
|
||||
}},
|
||||
}, nil
|
||||
return args.Get(0).(*worker.Stats), nil
|
||||
}
|
||||
|
||||
func (fc *fakeController) GetJobLogData(jobID string) ([]byte, error) {
|
||||
if jobID == "fake_job_ok" {
|
||||
return []byte("job log"), nil
|
||||
args := fc.Called(jobID)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return nil, errors.New("failed")
|
||||
return args.Get(0).([]byte), nil
|
||||
}
|
||||
|
||||
func createJobStats(name, kind, cron string) models.JobStats {
|
||||
now := time.Now()
|
||||
func (fc *fakeController) GetPeriodicExecutions(periodicJobID string, query *query.Parameter) ([]*job.Stats, int64, error) {
|
||||
args := fc.Called(periodicJobID, query)
|
||||
if args.Error(2) != nil {
|
||||
return nil, args.Get(1).(int64), args.Error(2)
|
||||
}
|
||||
|
||||
return models.JobStats{
|
||||
Stats: &models.JobStatData{
|
||||
JobID: "fake_ID_ok",
|
||||
Status: "pending",
|
||||
return args.Get(0).([]*job.Stats), args.Get(1).(int64), nil
|
||||
}
|
||||
|
||||
func (fc *fakeController) ScheduledJobs(query *query.Parameter) ([]*job.Stats, int64, error) {
|
||||
args := fc.Called(query)
|
||||
if args.Error(2) != nil {
|
||||
return nil, args.Get(1).(int64), args.Error(2)
|
||||
}
|
||||
|
||||
return args.Get(0).([]*job.Stats), args.Get(1).(int64), nil
|
||||
}
|
||||
|
||||
func createJobStats(name, kind, cron string) *job.Stats {
|
||||
now := time.Now()
|
||||
params := make(job.Parameters)
|
||||
params["image"] = "testing:v1"
|
||||
|
||||
return &job.Stats{
|
||||
Info: &job.StatsInfo{
|
||||
JobID: "fake_job_ID",
|
||||
Status: job.PendingStatus.String(),
|
||||
JobName: name,
|
||||
JobKind: kind,
|
||||
IsUnique: false,
|
||||
RefLink: "/api/v1/jobs/fake_ID_ok",
|
||||
RefLink: "/api/v1/jobs/fake_job_ID",
|
||||
CronSpec: cron,
|
||||
RunAt: now.Add(100 * time.Second).Unix(),
|
||||
EnqueueTime: now.Unix(),
|
||||
UpdateTime: now.Unix(),
|
||||
Parameters: params,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getResult(res []byte) (models.JobStats, error) {
|
||||
obj := models.JobStats{}
|
||||
err := json.Unmarshal(res, &obj)
|
||||
func getResult(res []byte) (*job.Stats, error) {
|
||||
obj := &job.Stats{}
|
||||
err := json.Unmarshal(res, obj)
|
||||
|
||||
return obj, err
|
||||
}
|
||||
|
||||
func createServer() (*Server, uint, *env.Context) {
|
||||
port := uint(30000 + rand.Intn(10000))
|
||||
config := ServerConfig{
|
||||
Protocol: "http",
|
||||
Port: port,
|
||||
func createJobReq() *job.Request {
|
||||
params := make(job.Parameters)
|
||||
params["image"] = "testing:v1"
|
||||
|
||||
return &job.Request{
|
||||
Job: &job.RequestBody{
|
||||
Name: "my-testing-job",
|
||||
Parameters: params,
|
||||
Metadata: &job.Metadata{
|
||||
JobKind: "Periodic",
|
||||
Cron: "5 * * * * *",
|
||||
IsUnique: true,
|
||||
},
|
||||
StatusHook: "http://localhost:39999",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createJobActionReq(action string) *job.ActionRequest {
|
||||
return &job.ActionRequest{
|
||||
Action: action,
|
||||
}
|
||||
ctx := &env.Context{
|
||||
SystemContext: context.Background(),
|
||||
WG: new(sync.WaitGroup),
|
||||
ErrorChan: make(chan error, 1),
|
||||
}
|
||||
server := NewServer(ctx, testingRouter, config)
|
||||
return server, port, ctx
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// NoElementsError is a pre defined error to describe the case that no elements got
|
||||
// ErrNoElements is a pre defined error to describe the case that no elements got
|
||||
// from the backend database.
|
||||
var NoElementsError = errors.New("no elements got from the backend")
|
||||
var ErrNoElements = errors.New("no elements got from the backend")
|
||||
|
||||
// HmSet sets the properties of hash map
|
||||
func HmSet(conn redis.Conn, key string, fieldAndValues ...interface{}) error {
|
||||
@ -145,10 +145,10 @@ func ZPopMin(conn redis.Conn, key string) (interface{}, error) {
|
||||
if zrangeReply != nil {
|
||||
if elements, ok := zrangeReply.([]interface{}); ok {
|
||||
if len(elements) == 0 {
|
||||
return nil, NoElementsError
|
||||
} else {
|
||||
return elements[0], nil
|
||||
return nil, ErrNoElements
|
||||
}
|
||||
|
||||
return elements[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,10 @@ package rds
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -31,10 +35,40 @@ type simpleStatusChange struct {
|
||||
JobID string
|
||||
}
|
||||
|
||||
func TestZPopMin(t *testing.T) {
|
||||
conn := pool.Get()
|
||||
// RdsUtilsTestSuite tests functions located in rds package
|
||||
type RdsUtilsTestSuite struct {
|
||||
suite.Suite
|
||||
pool *redis.Pool
|
||||
namespace string
|
||||
conn redis.Conn
|
||||
}
|
||||
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *RdsUtilsTestSuite) SetupSuite() {
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
}
|
||||
|
||||
// SetupTest prepares test cases
|
||||
func (suite *RdsUtilsTestSuite) SetupTest() {
|
||||
suite.conn = suite.pool.Get()
|
||||
}
|
||||
|
||||
// TearDownTest clears test cases
|
||||
func (suite *RdsUtilsTestSuite) TearDownTest() {
|
||||
suite.conn.Close()
|
||||
}
|
||||
|
||||
// TearDownSuite clears test suite
|
||||
func (suite *RdsUtilsTestSuite) TearDownSuite() {
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
tests.ClearAll(suite.namespace, conn)
|
||||
}
|
||||
|
||||
// TestZPopMin ...
|
||||
func (suite *RdsUtilsTestSuite) TestZPopMin() {
|
||||
s1 := &simpleStatusChange{"a"}
|
||||
s2 := &simpleStatusChange{"b"}
|
||||
|
||||
@ -42,31 +76,65 @@ func TestZPopMin(t *testing.T) {
|
||||
raw2, _ := json.Marshal(s2)
|
||||
|
||||
key := KeyStatusUpdateRetryQueue(namespace)
|
||||
_, err := conn.Do("ZADD", key, time.Now().Unix(), raw1)
|
||||
_, err = conn.Do("ZADD", key, time.Now().Unix()+5, raw2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := suite.conn.Do("ZADD", key, time.Now().Unix(), raw1)
|
||||
_, err = suite.conn.Do("ZADD", key, time.Now().Unix()+5, raw2)
|
||||
require.Nil(suite.T(), err, "zadd objects error should be nil")
|
||||
|
||||
v, err := ZPopMin(conn, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v, err := ZPopMin(suite.conn, key)
|
||||
require.Nil(suite.T(), err, "nil error should be returned by calling ZPopMin")
|
||||
|
||||
change1 := &simpleStatusChange{}
|
||||
json.Unmarshal(v.([]byte), change1)
|
||||
if change1.JobID != "a" {
|
||||
t.Errorf("expect min element 'a' but got '%s'", change1.JobID)
|
||||
}
|
||||
assert.Equal(suite.T(), "a", change1.JobID, "job ID not equal")
|
||||
|
||||
v, err = ZPopMin(conn, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v, err = ZPopMin(suite.conn, key)
|
||||
require.Nil(suite.T(), err, "nil error should be returned by calling ZPopMin")
|
||||
|
||||
change2 := &simpleStatusChange{}
|
||||
json.Unmarshal(v.([]byte), change2)
|
||||
if change2.JobID != "b" {
|
||||
t.Errorf("expect min element 'b' but got '%s'", change2.JobID)
|
||||
}
|
||||
assert.Equal(suite.T(), "b", change2.JobID, "job ID not equal")
|
||||
}
|
||||
|
||||
// TestHmGetAndSet ...
|
||||
func (suite *RdsUtilsTestSuite) TestHmGetAndSet() {
|
||||
key := KeyJobStats(suite.namespace, "fake_job_id")
|
||||
err := HmSet(suite.conn, key, "a", "hello", "b", 100)
|
||||
require.Nil(suite.T(), err, "nil error should be returned for HmSet")
|
||||
|
||||
values, err := HmGet(suite.conn, key, "a", "b")
|
||||
require.Nil(suite.T(), err, "nil error should be returned for HmGet")
|
||||
assert.Equal(suite.T(), 2, len(values), "two values should be returned")
|
||||
assert.Equal(suite.T(), string(values[0].([]byte)), "hello")
|
||||
assert.Equal(suite.T(), string(values[1].([]byte)), "100")
|
||||
}
|
||||
|
||||
// TestAcquireAndReleaseLock ...
|
||||
func (suite *RdsUtilsTestSuite) TestAcquireAndReleaseLock() {
|
||||
key := KeyPeriodicLock(suite.namespace)
|
||||
err := AcquireLock(suite.conn, key, "RdsUtilsTestSuite", 60)
|
||||
assert.Nil(suite.T(), err, "nil error should be returned for 1st acquiring lock")
|
||||
|
||||
err = AcquireLock(suite.conn, key, "RdsUtilsTestSuite", 60)
|
||||
assert.NotNil(suite.T(), err, "non nil error should be returned for 2nd acquiring lock")
|
||||
|
||||
err = ReleaseLock(suite.conn, key, "RdsUtilsTestSuite")
|
||||
assert.Nil(suite.T(), err, "nil error should be returned for releasing lock")
|
||||
}
|
||||
|
||||
// TestGetZsetByScore ...
|
||||
func (suite *RdsUtilsTestSuite) TestGetZsetByScore() {
|
||||
key := KeyPeriod(suite.namespace)
|
||||
|
||||
count, err := suite.conn.Do("ZADD", key, 1, "hello", 2, "world")
|
||||
require.Nil(suite.T(), err, "nil error should be returned when adding prepared data by ZADD")
|
||||
require.Equal(suite.T(), int64(2), count.(int64), "two items should be added")
|
||||
|
||||
datas, err := GetZsetByScore(suite.conn, key, []int64{0, 2})
|
||||
require.Nil(suite.T(), err, "nil error should be returned when getting data with scores")
|
||||
assert.Equal(suite.T(), 2, len(datas), "expected 2 items but got %d", len(datas))
|
||||
}
|
||||
|
||||
// TestRdsUtilsTestSuite is suite entry for 'go test'
|
||||
func TestRdsUtilsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(RdsUtilsTestSuite))
|
||||
}
|
||||
|
@ -14,97 +14,108 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigLoadingFailed(t *testing.T) {
|
||||
cfg := &Configuration{}
|
||||
if err := cfg.Load("./config.not-existing.yaml", false); err == nil {
|
||||
t.Fatalf("Load config from none-existing document, expect none nil error but got '%s'\n", err)
|
||||
}
|
||||
// ConfigurationTestSuite tests the configuration loading
|
||||
type ConfigurationTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestConfigLoadingSucceed(t *testing.T) {
|
||||
cfg := &Configuration{}
|
||||
if err := cfg.Load("../config_test.yml", false); err != nil {
|
||||
t.Fatalf("Load config from yaml file, expect nil error but got error '%s'\n", err)
|
||||
}
|
||||
// TestConfigurationTestSuite is suite entry for 'go test'
|
||||
func TestConfigurationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ConfigurationTestSuite))
|
||||
}
|
||||
|
||||
func TestConfigLoadingWithEnv(t *testing.T) {
|
||||
// TestConfigLoadingFailed ...
|
||||
func (suite *ConfigurationTestSuite) TestConfigLoadingFailed() {
|
||||
cfg := &Configuration{}
|
||||
err := cfg.Load("./config.not-existing.yaml", false)
|
||||
assert.NotNil(suite.T(), err, "load config from none-existing document, expect none nil error but got nil")
|
||||
}
|
||||
|
||||
// TestConfigLoadingSucceed ...
|
||||
func (suite *ConfigurationTestSuite) TestConfigLoadingSucceed() {
|
||||
cfg := &Configuration{}
|
||||
err := cfg.Load("../config_test.yml", false)
|
||||
assert.Nil(suite.T(), err, "Load config from yaml file, expect nil error but got error '%s'", err)
|
||||
}
|
||||
|
||||
// TestConfigLoadingWithEnv ...
|
||||
func (suite *ConfigurationTestSuite) TestConfigLoadingWithEnv() {
|
||||
setENV()
|
||||
defer unsetENV()
|
||||
|
||||
cfg := &Configuration{}
|
||||
if err := cfg.Load("../config_test.yml", true); err != nil {
|
||||
t.Fatalf("Load config from yaml file, expect nil error but got error '%s'\n", err)
|
||||
}
|
||||
err := cfg.Load("../config_test.yml", true)
|
||||
require.Nil(suite.T(), err, "load config from yaml file, expect nil error but got error '%s'", err)
|
||||
|
||||
if cfg.Protocol != "https" {
|
||||
t.Errorf("expect protocol 'https', but got '%s'\n", cfg.Protocol)
|
||||
}
|
||||
if cfg.Port != 8989 {
|
||||
t.Errorf("expect port 8989 but got '%d'\n", cfg.Port)
|
||||
}
|
||||
if cfg.PoolConfig.WorkerCount != 8 {
|
||||
t.Errorf("expect workcount 8 but go '%d'\n", cfg.PoolConfig.WorkerCount)
|
||||
}
|
||||
if cfg.PoolConfig.RedisPoolCfg.RedisURL != "redis://arbitrary_username:password@8.8.8.8:6379/0" {
|
||||
t.Errorf("expect redis URL 'localhost' but got '%s'\n", cfg.PoolConfig.RedisPoolCfg.RedisURL)
|
||||
}
|
||||
if cfg.PoolConfig.RedisPoolCfg.Namespace != "ut_namespace" {
|
||||
t.Errorf("expect redis namespace 'ut_namespace' but got '%s'\n", cfg.PoolConfig.RedisPoolCfg.Namespace)
|
||||
}
|
||||
if GetAuthSecret() != "js_secret" {
|
||||
t.Errorf("expect auth secret 'js_secret' but got '%s'", GetAuthSecret())
|
||||
}
|
||||
if GetUIAuthSecret() != "core_secret" {
|
||||
t.Errorf("expect auth secret 'core_secret' but got '%s'", GetUIAuthSecret())
|
||||
}
|
||||
|
||||
unsetENV()
|
||||
assert.Equal(suite.T(), "https", cfg.Protocol, "expect protocol 'https', but got '%s'", cfg.Protocol)
|
||||
assert.Equal(suite.T(), uint(8989), cfg.Port, "expect port 8989 but got '%d'", cfg.Port)
|
||||
assert.Equal(
|
||||
suite.T(),
|
||||
uint(8),
|
||||
cfg.PoolConfig.WorkerCount,
|
||||
"expect worker count 8 but go '%d'",
|
||||
cfg.PoolConfig.WorkerCount,
|
||||
)
|
||||
assert.Equal(
|
||||
suite.T(),
|
||||
"redis://arbitrary_username:password@8.8.8.8:6379/0",
|
||||
cfg.PoolConfig.RedisPoolCfg.RedisURL,
|
||||
"expect redis URL 'localhost' but got '%s'",
|
||||
cfg.PoolConfig.RedisPoolCfg.RedisURL,
|
||||
)
|
||||
assert.Equal(
|
||||
suite.T(),
|
||||
"ut_namespace",
|
||||
cfg.PoolConfig.RedisPoolCfg.Namespace,
|
||||
"expect redis namespace 'ut_namespace' but got '%s'",
|
||||
cfg.PoolConfig.RedisPoolCfg.Namespace,
|
||||
)
|
||||
assert.Equal(suite.T(), "js_secret", GetAuthSecret(), "expect auth secret 'js_secret' but got '%s'", GetAuthSecret())
|
||||
assert.Equal(suite.T(), "core_secret", GetUIAuthSecret(), "expect auth secret 'core_secret' but got '%s'", GetUIAuthSecret())
|
||||
}
|
||||
|
||||
func TestDefaultConfig(t *testing.T) {
|
||||
if err := DefaultConfig.Load("../config_test.yml", true); err != nil {
|
||||
t.Fatalf("Load config from yaml file, expect nil error but got error '%s'\n", err)
|
||||
}
|
||||
// TestDefaultConfig ...
|
||||
func (suite *ConfigurationTestSuite) TestDefaultConfig() {
|
||||
err := DefaultConfig.Load("../config_test.yml", true)
|
||||
require.Nil(suite.T(), err, "load config from yaml file, expect nil error but got error '%s'", err)
|
||||
|
||||
redisURL := DefaultConfig.PoolConfig.RedisPoolCfg.RedisURL
|
||||
if redisURL != "redis://localhost:6379" {
|
||||
t.Errorf("expect redisURL '%s' but got '%s'\n", "redis://localhost:6379", redisURL)
|
||||
}
|
||||
assert.Equal(suite.T(), "redis://localhost:6379", redisURL, "expect redisURL '%s' but got '%s'", "redis://localhost:6379", redisURL)
|
||||
|
||||
if len(DefaultConfig.JobLoggerConfigs) == 0 {
|
||||
t.Errorf("expect 2 job loggers configured but got %d", len(DefaultConfig.JobLoggerConfigs))
|
||||
}
|
||||
jLoggerCount := len(DefaultConfig.JobLoggerConfigs)
|
||||
assert.Equal(suite.T(), 2, jLoggerCount, "expect 2 job loggers configured but got %d", jLoggerCount)
|
||||
|
||||
if len(DefaultConfig.LoggerConfigs) == 0 {
|
||||
t.Errorf("expect 1 loggers configured but got %d", len(DefaultConfig.LoggerConfigs))
|
||||
}
|
||||
loggerCount := len(DefaultConfig.LoggerConfigs)
|
||||
assert.Equal(suite.T(), 1, loggerCount, "expect 1 loggers configured but got %d", loggerCount)
|
||||
|
||||
// Only verify the complicated one
|
||||
theLogger := DefaultConfig.JobLoggerConfigs[1]
|
||||
if theLogger.Name != "FILE" {
|
||||
t.Fatalf("expect FILE logger but got %s", theLogger.Name)
|
||||
}
|
||||
if theLogger.Level != "INFO" {
|
||||
t.Errorf("expect INFO log level of FILE logger but got %s", theLogger.Level)
|
||||
}
|
||||
if len(theLogger.Settings) == 0 {
|
||||
t.Errorf("expect extra settings but got nothing")
|
||||
}
|
||||
if theLogger.Settings["base_dir"] != "/tmp/job_logs" {
|
||||
t.Errorf("expect extra setting base_dir to be '/tmp/job_logs' but got %s", theLogger.Settings["base_dir"])
|
||||
}
|
||||
if theLogger.Sweeper == nil {
|
||||
t.Fatalf("expect non nil sweeper of FILE logger but got nil")
|
||||
}
|
||||
if theLogger.Sweeper.Duration != 5 {
|
||||
t.Errorf("expect sweep duration to be 5 but got %d", theLogger.Sweeper.Duration)
|
||||
}
|
||||
if theLogger.Sweeper.Settings["work_dir"] != "/tmp/job_logs" {
|
||||
t.Errorf("expect work dir of sweeper of FILE logger to be '/tmp/job_logs' but got %s", theLogger.Sweeper.Settings["work_dir"])
|
||||
}
|
||||
assert.Equal(suite.T(), "FILE", theLogger.Name, "expect FILE logger but got %s", theLogger.Name)
|
||||
assert.Equal(suite.T(), "INFO", theLogger.Level, "expect INFO log level of FILE logger but got %s", theLogger.Level)
|
||||
assert.NotEqual(suite.T(), 0, len(theLogger.Settings), "expect extra settings but got nothing")
|
||||
assert.Equal(
|
||||
suite.T(),
|
||||
"/tmp/job_logs",
|
||||
theLogger.Settings["base_dir"],
|
||||
"expect extra setting base_dir to be '/tmp/job_logs' but got %s",
|
||||
theLogger.Settings["base_dir"],
|
||||
)
|
||||
assert.NotNil(suite.T(), theLogger.Sweeper, "expect non nil sweeper of FILE logger but got nil")
|
||||
assert.Equal(suite.T(), 5, theLogger.Sweeper.Duration, "expect sweep duration to be 5 but got %d", theLogger.Sweeper.Duration)
|
||||
assert.Equal(
|
||||
suite.T(),
|
||||
"/tmp/job_logs",
|
||||
theLogger.Sweeper.Settings["work_dir"],
|
||||
"expect work dir of sweeper of FILE logger to be '/tmp/job_logs' but got %s",
|
||||
theLogger.Sweeper.Settings["work_dir"],
|
||||
)
|
||||
}
|
||||
|
||||
func setENV() {
|
||||
|
@ -20,7 +20,7 @@ worker_pool:
|
||||
#redis://[arbitrary_username:password@]ipaddress:port/database_index
|
||||
#or ipaddress:port[,weight,password,database_index]
|
||||
redis_url: "localhost:6379"
|
||||
namespace: "harbor_job_service"
|
||||
namespace: "testing_job_service_v2"
|
||||
|
||||
#Loggers for the running job
|
||||
job_loggers:
|
||||
|
@ -14,312 +14,346 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/query"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/job/impl/sample"
|
||||
"github.com/goharbor/harbor/src/jobservice/worker"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/models"
|
||||
)
|
||||
|
||||
func TestLaunchGenericJob(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
req := createJobReq("Generic", false, false)
|
||||
res, err := c.LaunchJob(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// ControllerTestSuite tests functions of core controller
|
||||
type ControllerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
if res.Stats.JobID != "fake_ID" {
|
||||
t.Fatalf("expect enqueued job ID 'fake_ID' but got '%s'\n", res.Stats.JobID)
|
||||
}
|
||||
lcmCtl *fakeLcmController
|
||||
worker *fakeWorker
|
||||
ctl Interface
|
||||
|
||||
res *job.Stats
|
||||
jobID string
|
||||
params job.Parameters
|
||||
}
|
||||
|
||||
func TestLaunchGenericJobUnique(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
req := createJobReq("Generic", true, false)
|
||||
res, err := c.LaunchJob(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *ControllerTestSuite) SetupSuite() {
|
||||
suite.ctl = NewController(suite, suite)
|
||||
|
||||
if res.Stats.JobID != "fake_ID" {
|
||||
t.Fatalf("expect enqueued job ID 'fake_ID' but got '%s'\n", res.Stats.JobID)
|
||||
}
|
||||
}
|
||||
suite.params = make(job.Parameters)
|
||||
suite.params["name"] = "testing:v1"
|
||||
|
||||
func TestLaunchGenericJobWithHook(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
req := createJobReq("Generic", false, true)
|
||||
res, err := c.LaunchJob(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.Stats.JobID != "fake_ID" {
|
||||
t.Fatalf("expect enqueued job ID 'fake_ID' but got '%s'\n", res.Stats.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLaunchScheduledJob(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
req := createJobReq("Scheduled", false, true)
|
||||
res, err := c.LaunchJob(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.Stats.JobID != "fake_ID_Scheduled" {
|
||||
t.Fatalf("expect enqueued job ID 'fake_ID_Scheduled' but got '%s'\n", res.Stats.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLaunchScheduledUniqueJob(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
req := createJobReq("Scheduled", true, false)
|
||||
res, err := c.LaunchJob(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.Stats.JobID != "fake_ID_Scheduled" {
|
||||
t.Fatalf("expect enqueued job ID 'fake_ID_Scheduled' but got '%s'\n", res.Stats.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLaunchPeriodicJob(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
req := createJobReq("Periodic", true, false)
|
||||
res, err := c.LaunchJob(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.Stats.JobID != "fake_ID_Periodic" {
|
||||
t.Fatalf("expect enqueued job ID 'fake_ID_Periodic' but got '%s'\n", res.Stats.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJobStats(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
stats, err := c.GetJob("fake_ID")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if stats.Stats.Status != "running" {
|
||||
t.Fatalf("expect stauts 'running' but got '%s'\n", stats.Stats.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobActions(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
|
||||
if err := c.StopJob("fake_ID"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := c.CancelJob("fake_ID"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := c.RetryJob("fake_ID"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJobLogData(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
|
||||
if _, err := c.GetJobLogData("fake_ID"); err == nil {
|
||||
t.Fatal("expect error but got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckStatus(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
|
||||
st, err := c.CheckStatus()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(st.Pools) == 0 {
|
||||
t.Fatal("expect status data but got zero list")
|
||||
}
|
||||
|
||||
if st.Pools[0].Status != "running" {
|
||||
t.Fatalf("expect status 'running' but got '%s'\n", st.Pools[0].Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidCheck(t *testing.T) {
|
||||
pool := &fakePool{}
|
||||
c := NewController(pool)
|
||||
|
||||
req := models.JobRequest{
|
||||
Job: &models.JobData{
|
||||
Name: "DEMO",
|
||||
Metadata: &models.JobMetadata{
|
||||
JobKind: "kind",
|
||||
},
|
||||
suite.jobID = utils.MakeIdentifier()
|
||||
suite.res = &job.Stats{
|
||||
Info: &job.StatsInfo{
|
||||
JobID: suite.jobID,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := c.LaunchJob(req); err == nil {
|
||||
t.Fatal("error expected but got nil")
|
||||
}
|
||||
|
||||
req.Job.Name = "fake"
|
||||
if _, err := c.LaunchJob(req); err == nil {
|
||||
t.Fatal("error expected but got nil")
|
||||
}
|
||||
|
||||
req.Job.Metadata.JobKind = "Scheduled"
|
||||
if _, err := c.LaunchJob(req); err == nil {
|
||||
t.Fatal("error expected but got nil")
|
||||
}
|
||||
|
||||
req.Job.Metadata.JobKind = "Periodic"
|
||||
req.Job.Metadata.Cron = "x x x x x x"
|
||||
if _, err := c.LaunchJob(req); err == nil {
|
||||
t.Fatal("error expected but got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func createJobReq(kind string, isUnique bool, withHook bool) models.JobRequest {
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing"
|
||||
req := models.JobRequest{
|
||||
Job: &models.JobData{
|
||||
Name: "DEMO",
|
||||
// Prepare for each test case
|
||||
func (suite *ControllerTestSuite) SetupTest() {
|
||||
suite.worker = &fakeWorker{}
|
||||
suite.lcmCtl = &fakeLcmController{}
|
||||
|
||||
suite.lcmCtl.On("Track", suite.jobID).Return(job.NewBasicTrackerWithStats(nil, suite.res, "ns", nil, nil), nil)
|
||||
suite.lcmCtl.On("New", suite.res).Return(job.NewBasicTrackerWithStats(nil, suite.res, "ns", nil, nil), nil)
|
||||
|
||||
suite.worker.On("IsKnownJob", job.SampleJob).Return((*sample.Job)(nil), true)
|
||||
suite.worker.On("IsKnownJob", "fake").Return(nil, false)
|
||||
suite.worker.On("ValidateJobParameters", (*sample.Job)(nil), suite.params).Return(nil)
|
||||
}
|
||||
|
||||
// TestControllerTestSuite is suite entry for 'go test'
|
||||
func TestControllerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ControllerTestSuite))
|
||||
}
|
||||
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *ControllerTestSuite) TestLaunchGenericJob() {
|
||||
req := createJobReq("Generic")
|
||||
|
||||
suite.worker.On("Enqueue", job.SampleJob, suite.params, true, req.Job.StatusHook).Return(suite.res, nil)
|
||||
|
||||
res, err := suite.ctl.LaunchJob(req)
|
||||
require.Nil(suite.T(), err, "launch job: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), suite.jobID, res.Info.JobID, "mismatch job ID")
|
||||
}
|
||||
|
||||
// TestLaunchScheduledJob ...
|
||||
func (suite *ControllerTestSuite) TestLaunchScheduledJob() {
|
||||
req := createJobReq("Scheduled")
|
||||
|
||||
suite.worker.On("Schedule", job.SampleJob, suite.params, uint64(100), true, req.Job.StatusHook).Return(suite.res, nil)
|
||||
|
||||
res, err := suite.ctl.LaunchJob(req)
|
||||
require.Nil(suite.T(), err, "launch scheduled job: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), suite.jobID, res.Info.JobID, "mismatch job ID")
|
||||
}
|
||||
|
||||
// TestLaunchPeriodicJob ...
|
||||
func (suite *ControllerTestSuite) TestLaunchPeriodicJob() {
|
||||
req := createJobReq("Periodic")
|
||||
|
||||
suite.worker.On("PeriodicallyEnqueue", job.SampleJob, suite.params, "5 * * * * *", true, req.Job.StatusHook).Return(suite.res, nil)
|
||||
|
||||
res, err := suite.ctl.LaunchJob(req)
|
||||
require.Nil(suite.T(), err, "launch periodic job: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), suite.jobID, res.Info.JobID, "mismatch job ID")
|
||||
}
|
||||
|
||||
// TestGetJobStats ...
|
||||
func (suite *ControllerTestSuite) TestGetJobStats() {
|
||||
res, err := suite.ctl.GetJob(suite.jobID)
|
||||
require.Nil(suite.T(), err, "get job stats: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), suite.jobID, res.Info.JobID, "mismatch job ID")
|
||||
}
|
||||
|
||||
// TestJobActions ...
|
||||
func (suite *ControllerTestSuite) TestJobActions() {
|
||||
suite.worker.On("StopJob", suite.jobID).Return(nil)
|
||||
suite.worker.On("RetryJob", suite.jobID).Return(nil)
|
||||
|
||||
err := suite.ctl.StopJob(suite.jobID)
|
||||
err = suite.ctl.RetryJob(suite.jobID)
|
||||
|
||||
assert.Nil(suite.T(), err, "job action: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// TestCheckStatus ...
|
||||
func (suite *ControllerTestSuite) TestCheckStatus() {
|
||||
suite.worker.On("Stats").Return(&worker.Stats{
|
||||
[]*worker.StatsData{
|
||||
{
|
||||
Status: "running",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
st, err := suite.ctl.CheckStatus()
|
||||
require.Nil(suite.T(), err, "check worker status: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), 1, len(st.Pools), "expected 1 pool status but got 0")
|
||||
assert.Equal(suite.T(), "running", st.Pools[0].Status, "expected running pool but got %s", st.Pools[0].Status)
|
||||
}
|
||||
|
||||
// TestScheduledJobs ...
|
||||
func (suite *ControllerTestSuite) TestScheduledJobs() {
|
||||
q := &query.Parameter{
|
||||
PageSize: 20,
|
||||
PageNumber: 1,
|
||||
}
|
||||
|
||||
suite.worker.On("ScheduledJobs", q).Return([]*job.Stats{suite.res}, 1, nil)
|
||||
|
||||
_, total, err := suite.ctl.ScheduledJobs(q)
|
||||
require.Nil(suite.T(), err, "scheduled jobs: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), int64(1), total, "expected 1 item but got 0")
|
||||
}
|
||||
|
||||
// TestInvalidChecks ...
|
||||
func (suite *ControllerTestSuite) TestInvalidChecks() {
|
||||
req := createJobReq("kind")
|
||||
|
||||
_, err := suite.ctl.LaunchJob(req)
|
||||
assert.NotNil(suite.T(), err, "invalid job kind: error expected but got nil")
|
||||
|
||||
req.Job.Metadata.JobKind = job.KindGeneric
|
||||
req.Job.Name = "fake"
|
||||
_, err = suite.ctl.LaunchJob(req)
|
||||
assert.NotNil(suite.T(), err, "invalid job name: error expected but got nil")
|
||||
|
||||
req.Job.Metadata.JobKind = job.KindScheduled
|
||||
req.Job.Name = job.SampleJob
|
||||
req.Job.Metadata.ScheduleDelay = 0
|
||||
_, err = suite.ctl.LaunchJob(req)
|
||||
assert.NotNil(suite.T(), err, "invalid scheduled job: error expected but got nil")
|
||||
|
||||
req.Job.Metadata.JobKind = job.KindPeriodic
|
||||
req.Job.Metadata.Cron = "x x x x x x"
|
||||
_, err = suite.ctl.LaunchJob(req)
|
||||
assert.NotNil(suite.T(), err, "invalid job name: error expected but got nil")
|
||||
}
|
||||
|
||||
func createJobReq(kind string) *job.Request {
|
||||
params := make(job.Parameters)
|
||||
params["name"] = "testing:v1"
|
||||
return &job.Request{
|
||||
Job: &job.RequestBody{
|
||||
Name: job.SampleJob,
|
||||
Parameters: params,
|
||||
Metadata: &models.JobMetadata{
|
||||
StatusHook: "http://localhost:9090",
|
||||
Metadata: &job.Metadata{
|
||||
JobKind: kind,
|
||||
IsUnique: isUnique,
|
||||
IsUnique: true,
|
||||
ScheduleDelay: 100,
|
||||
Cron: "5 * * * * *",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if withHook {
|
||||
req.Job.StatusHook = "http://localhost:9090"
|
||||
// Implement lcm controller interface
|
||||
func (suite *ControllerTestSuite) Serve() error {
|
||||
return suite.lcmCtl.Serve()
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) New(stats *job.Stats) (job.Tracker, error) {
|
||||
return suite.lcmCtl.New(stats)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) Track(jobID string) (job.Tracker, error) {
|
||||
return suite.lcmCtl.Track(jobID)
|
||||
}
|
||||
|
||||
// Implement worker interface
|
||||
func (suite *ControllerTestSuite) Start() error {
|
||||
return suite.worker.Start()
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) RegisterJobs(jobs map[string]interface{}) error {
|
||||
return suite.worker.RegisterJobs(jobs)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) Enqueue(jobName string, params job.Parameters, isUnique bool, webHook string) (*job.Stats, error) {
|
||||
return suite.worker.Enqueue(jobName, params, isUnique, webHook)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) Schedule(jobName string, params job.Parameters, runAfterSeconds uint64, isUnique bool, webHook string) (*job.Stats, error) {
|
||||
return suite.worker.Schedule(jobName, params, runAfterSeconds, isUnique, webHook)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) PeriodicallyEnqueue(jobName string, params job.Parameters, cronSetting string, isUnique bool, webHook string) (*job.Stats, error) {
|
||||
return suite.worker.PeriodicallyEnqueue(jobName, params, cronSetting, isUnique, webHook)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) Stats() (*worker.Stats, error) {
|
||||
return suite.worker.Stats()
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) IsKnownJob(name string) (interface{}, bool) {
|
||||
return suite.worker.IsKnownJob(name)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) ValidateJobParameters(jobType interface{}, params job.Parameters) error {
|
||||
return suite.worker.ValidateJobParameters(jobType, params)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) StopJob(jobID string) error {
|
||||
return suite.worker.StopJob(jobID)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) RetryJob(jobID string) error {
|
||||
return suite.worker.RetryJob(jobID)
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) ScheduledJobs(query *query.Parameter) ([]*job.Stats, int64, error) {
|
||||
return suite.worker.ScheduledJobs(query)
|
||||
}
|
||||
|
||||
// Implement fake objects with mock
|
||||
type fakeLcmController struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (flc *fakeLcmController) Serve() error {
|
||||
return flc.Called().Error(0)
|
||||
}
|
||||
|
||||
func (flc *fakeLcmController) New(stats *job.Stats) (job.Tracker, error) {
|
||||
args := flc.Called(stats)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return req
|
||||
return args.Get(0).(job.Tracker), nil
|
||||
}
|
||||
|
||||
type fakePool struct{}
|
||||
|
||||
func (f *fakePool) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePool) RegisterJob(name string, job interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePool) RegisterJobs(jobs map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePool) Enqueue(jobName string, params models.Parameters, isUnique bool) (models.JobStats, error) {
|
||||
return models.JobStats{
|
||||
Stats: &models.JobStatData{
|
||||
JobID: "fake_ID",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *fakePool) Schedule(jobName string, params models.Parameters, runAfterSeconds uint64, isUnique bool) (models.JobStats, error) {
|
||||
return models.JobStats{
|
||||
Stats: &models.JobStatData{
|
||||
JobID: "fake_ID_Scheduled",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *fakePool) PeriodicallyEnqueue(jobName string, params models.Parameters, cronSetting string) (models.JobStats, error) {
|
||||
return models.JobStats{
|
||||
Stats: &models.JobStatData{
|
||||
JobID: "fake_ID_Periodic",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *fakePool) Stats() (models.JobPoolStats, error) {
|
||||
return models.JobPoolStats{
|
||||
Pools: []*models.JobPoolStatsData{
|
||||
{
|
||||
Status: "running",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *fakePool) IsKnownJob(name string) (interface{}, bool) {
|
||||
return (*fakeJob)(nil), true
|
||||
}
|
||||
|
||||
func (f *fakePool) ValidateJobParameters(jobType interface{}, params map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePool) GetJobStats(jobID string) (models.JobStats, error) {
|
||||
return models.JobStats{
|
||||
Stats: &models.JobStatData{
|
||||
JobID: "fake_ID",
|
||||
Status: "running",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *fakePool) StopJob(jobID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePool) CancelJob(jobID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePool) RetryJob(jobID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePool) RegisterHook(jobID string, hookURL string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeJob struct{}
|
||||
|
||||
func (j *fakeJob) MaxFails() uint {
|
||||
return 3
|
||||
}
|
||||
|
||||
func (j *fakeJob) ShouldRetry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (j *fakeJob) Validate(params map[string]interface{}) error {
|
||||
if p, ok := params["name"]; ok {
|
||||
if p == "testing" {
|
||||
return nil
|
||||
}
|
||||
func (flc *fakeLcmController) Track(jobID string) (job.Tracker, error) {
|
||||
args := flc.Called(jobID)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return errors.New("testing error")
|
||||
return args.Get(0).(job.Tracker), nil
|
||||
}
|
||||
|
||||
func (j *fakeJob) Run(ctx env.JobContext, params map[string]interface{}) error {
|
||||
return nil
|
||||
type fakeWorker struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (f *fakeWorker) Start() error {
|
||||
return f.Called().Error(0)
|
||||
}
|
||||
|
||||
func (f *fakeWorker) RegisterJobs(jobs map[string]interface{}) error {
|
||||
return f.Called(jobs).Error(0)
|
||||
}
|
||||
|
||||
func (f *fakeWorker) Enqueue(jobName string, params job.Parameters, isUnique bool, webHook string) (*job.Stats, error) {
|
||||
args := f.Called(jobName, params, isUnique, webHook)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return args.Get(0).(*job.Stats), nil
|
||||
}
|
||||
|
||||
func (f *fakeWorker) Schedule(jobName string, params job.Parameters, runAfterSeconds uint64, isUnique bool, webHook string) (*job.Stats, error) {
|
||||
args := f.Called(jobName, params, runAfterSeconds, isUnique, webHook)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return args.Get(0).(*job.Stats), nil
|
||||
}
|
||||
|
||||
func (f *fakeWorker) PeriodicallyEnqueue(jobName string, params job.Parameters, cronSetting string, isUnique bool, webHook string) (*job.Stats, error) {
|
||||
args := f.Called(jobName, params, cronSetting, isUnique, webHook)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return args.Get(0).(*job.Stats), nil
|
||||
}
|
||||
|
||||
func (f *fakeWorker) Stats() (*worker.Stats, error) {
|
||||
args := f.Called()
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return args.Get(0).(*worker.Stats), nil
|
||||
}
|
||||
|
||||
func (f *fakeWorker) IsKnownJob(name string) (interface{}, bool) {
|
||||
args := f.Called(name)
|
||||
if !args.Bool(1) {
|
||||
return nil, args.Bool(1)
|
||||
}
|
||||
|
||||
return args.Get(0), args.Bool(1)
|
||||
}
|
||||
|
||||
func (f *fakeWorker) ValidateJobParameters(jobType interface{}, params job.Parameters) error {
|
||||
return f.Called(jobType, params).Error(0)
|
||||
}
|
||||
|
||||
func (f *fakeWorker) StopJob(jobID string) error {
|
||||
return f.Called(jobID).Error(0)
|
||||
}
|
||||
|
||||
func (f *fakeWorker) RetryJob(jobID string) error {
|
||||
return f.Called(jobID).Error(0)
|
||||
}
|
||||
|
||||
func (f *fakeWorker) ScheduledJobs(query *query.Parameter) ([]*job.Stats, int64, error) {
|
||||
args := f.Called(query)
|
||||
if args.Error(2) != nil {
|
||||
return nil, 0, args.Error(2)
|
||||
}
|
||||
|
||||
return args.Get(0).([]*job.Stats), int64(args.Int(1)), nil
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ const (
|
||||
GetScheduledJobsErrorCode
|
||||
// GetPeriodicExecutionErrorCode is code for the error of getting periodic executions
|
||||
GetPeriodicExecutionErrorCode
|
||||
// StatusMismatchErrorCode is code for the error of mismatching status
|
||||
StatusMismatchErrorCode
|
||||
)
|
||||
|
||||
// baseError ...
|
||||
@ -193,20 +195,54 @@ func BadRequestError(object interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
// statusMismatchError is designed for the case of job status update mismatching
|
||||
type statusMismatchError struct {
|
||||
baseError
|
||||
}
|
||||
|
||||
// StatusMismatchError returns the error of job status mismatching
|
||||
func StatusMismatchError(current, target string) error {
|
||||
return statusMismatchError{
|
||||
baseError{
|
||||
Code: StatusMismatchErrorCode,
|
||||
Err: "mismatch job status",
|
||||
Description: fmt.Sprintf("current %s, setting to %s", current, target),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// IsObjectNotFoundError return true if the error is objectNotFoundError
|
||||
func IsObjectNotFoundError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := err.(objectNotFoundError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsConflictError returns true if the error is conflictError
|
||||
func IsConflictError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := err.(conflictError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsBadRequestError returns true if the error is badRequestError
|
||||
func IsBadRequestError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := err.(badRequestError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsStatusMismatchError returns true if the error is statusMismatchError
|
||||
func IsStatusMismatchError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := err.(statusMismatchError)
|
||||
return ok
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ func (ba *basicAgent) loopRetry() {
|
||||
<-token
|
||||
if err := ba.reSend(); err != nil {
|
||||
waitInterval := shortLoopInterval
|
||||
if err == rds.NoElementsError {
|
||||
if err == rds.ErrNoElements {
|
||||
// No elements
|
||||
waitInterval = longLoopInterval
|
||||
} else {
|
||||
|
@ -24,11 +24,59 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/common/rds"
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/lcm"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func TestEventSending(t *testing.T) {
|
||||
// HookAgentTestSuite tests functions of hook agent
|
||||
type HookAgentTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
pool *redis.Pool
|
||||
namespace string
|
||||
lcmCtl lcm.Controller
|
||||
|
||||
envContext *env.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// TestHookAgentTestSuite is entry of go test
|
||||
func TestHookAgentTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(HookAgentTestSuite))
|
||||
}
|
||||
|
||||
// SetupSuite prepares test suites
|
||||
func (suite *HookAgentTestSuite) SetupSuite() {
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
suite.envContext = &env.Context{
|
||||
SystemContext: ctx,
|
||||
WG: new(sync.WaitGroup),
|
||||
}
|
||||
suite.cancel = cancel
|
||||
|
||||
suite.lcmCtl = lcm.NewController(suite.envContext, suite.namespace, suite.pool, func(hookURL string, change *job.StatusChange) error { return nil })
|
||||
}
|
||||
|
||||
// TearDownSuite prepares test suites
|
||||
func (suite *HookAgentTestSuite) TearDownSuite() {
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
tests.ClearAll(suite.namespace, conn)
|
||||
}
|
||||
|
||||
// TestEventSending ...
|
||||
func (suite *HookAgentTestSuite) TestEventSending() {
|
||||
done := make(chan bool, 1)
|
||||
|
||||
expected := uint32(1300) // >1024 max
|
||||
@ -52,20 +100,13 @@ func TestEventSending(t *testing.T) {
|
||||
done <- true // time out
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
ns := tests.GiveMeTestNamespace()
|
||||
pool := tests.GiveMeRedisPool()
|
||||
|
||||
conn := pool.Get()
|
||||
defer tests.ClearAll(ns, conn)
|
||||
|
||||
agent := NewAgent(ctx, ns, pool)
|
||||
agent := NewAgent(suite.envContext, suite.namespace, suite.pool)
|
||||
agent.Attach(suite.lcmCtl)
|
||||
agent.Serve()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
cancel()
|
||||
suite.cancel()
|
||||
}()
|
||||
|
||||
for i := uint32(0); i < expected; i++ {
|
||||
@ -81,29 +122,22 @@ func TestEventSending(t *testing.T) {
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
if err := agent.Trigger(evt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := agent.Trigger(evt)
|
||||
require.Nil(suite.T(), err, "agent trigger: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// Check results
|
||||
<-done
|
||||
if count != expected {
|
||||
t.Fatalf("expected %d hook events but only got %d", expected, count)
|
||||
}
|
||||
require.Equal(suite.T(), expected, count, "expected %d hook events but only got %d", expected, count)
|
||||
}()
|
||||
|
||||
// Wait
|
||||
<-ctx.Done()
|
||||
suite.envContext.WG.Wait()
|
||||
}
|
||||
|
||||
func TestRetryAndPopMin(t *testing.T) {
|
||||
// TestRetryAndPopMin ...
|
||||
func (suite *HookAgentTestSuite) TestRetryAndPopMin() {
|
||||
ctx := context.Background()
|
||||
ns := tests.GiveMeTestNamespace()
|
||||
pool := tests.GiveMeRedisPool()
|
||||
|
||||
conn := pool.Get()
|
||||
defer tests.ClearAll(ns, conn)
|
||||
|
||||
tks := make(chan bool, maxHandlers)
|
||||
// Put tokens
|
||||
@ -113,12 +147,13 @@ func TestRetryAndPopMin(t *testing.T) {
|
||||
|
||||
agent := &basicAgent{
|
||||
context: ctx,
|
||||
namespace: ns,
|
||||
client: NewClient(),
|
||||
namespace: suite.namespace,
|
||||
client: NewClient(ctx),
|
||||
events: make(chan *Event, maxEventChanBuffer),
|
||||
tokens: tks,
|
||||
redisPool: pool,
|
||||
redisPool: suite.pool,
|
||||
}
|
||||
agent.Attach(suite.lcmCtl)
|
||||
|
||||
changeData := &job.StatusChange{
|
||||
JobID: "fake_job_ID",
|
||||
@ -133,45 +168,30 @@ func TestRetryAndPopMin(t *testing.T) {
|
||||
}
|
||||
|
||||
// Mock job stats
|
||||
conn = pool.Get()
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
key := rds.KeyJobStats(ns, "fake_job_ID")
|
||||
key := rds.KeyJobStats(suite.namespace, "fake_job_ID")
|
||||
_, err := conn.Do("HSET", key, "status", job.SuccessStatus.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.Nil(suite.T(), err, "prepare job stats: nil error returned but got %s", err)
|
||||
|
||||
if err := agent.pushForRetry(evt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = agent.pushForRetry(evt)
|
||||
require.Nil(suite.T(), err, "push for retry: nil error expected but got %s", err)
|
||||
|
||||
if err := agent.popMinOnes(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check results
|
||||
if len(agent.events) > 0 {
|
||||
t.Error("the hook event should be discard but actually not")
|
||||
}
|
||||
err = agent.reSend()
|
||||
require.Error(suite.T(), err, "resend: non nil error expected but got nil")
|
||||
assert.Equal(suite.T(), 0, len(agent.events), "the hook event should be discard but actually not")
|
||||
|
||||
// Change status
|
||||
_, err = conn.Do("HSET", key, "status", job.PendingStatus.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.Nil(suite.T(), err, "prepare job stats: nil error returned but got %s", err)
|
||||
|
||||
if err := agent.pushForRetry(evt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := agent.popMinOnes(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = agent.pushForRetry(evt)
|
||||
require.Nil(suite.T(), err, "push for retry: nil error expected but got %s", err)
|
||||
err = agent.reSend()
|
||||
require.Nil(suite.T(), err, "resend: nil error should be returned but got %s", err)
|
||||
|
||||
<-time.After(time.Duration(1) * time.Second)
|
||||
|
||||
if len(agent.events) != 1 {
|
||||
t.Errorf("the hook event should be requeued but actually not: %d", len(agent.events))
|
||||
}
|
||||
assert.Equal(suite.T(), 1, len(agent.events), "the hook event should be requeued but actually not: %d", len(agent.events))
|
||||
}
|
||||
|
@ -14,58 +14,92 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var testClient = NewClient()
|
||||
// HookClientTestSuite tests functions of hook client
|
||||
type HookClientTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
mockServer *httptest.Server
|
||||
client Client
|
||||
}
|
||||
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *HookClientTestSuite) SetupSuite() {
|
||||
suite.client = NewClient(context.Background())
|
||||
suite.mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
bytes, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
m := &Event{}
|
||||
err = json.Unmarshal(bytes, m)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if m.Data.JobID == "job_ID_failed" {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
func TestHookClient(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "ok")
|
||||
}))
|
||||
defer ts.Close()
|
||||
}
|
||||
|
||||
// TearDownSuite clears test suite
|
||||
func (suite *HookClientTestSuite) TearDownSuite() {
|
||||
suite.mockServer.Close()
|
||||
}
|
||||
|
||||
// TestHookClientTestSuite is entry of go test
|
||||
func TestHookClientTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(HookClientTestSuite))
|
||||
}
|
||||
|
||||
// TestHookClient ...
|
||||
func (suite *HookClientTestSuite) TestHookClient() {
|
||||
changeData := &job.StatusChange{
|
||||
JobID: "fake_job_ID",
|
||||
Status: "running",
|
||||
}
|
||||
evt := &Event{
|
||||
URL: ts.URL,
|
||||
URL: suite.mockServer.URL,
|
||||
Data: changeData,
|
||||
Message: fmt.Sprintf("Status of job %s changed to: %s", changeData.JobID, changeData.Status),
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
err := testClient.SendEvent(evt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := suite.client.SendEvent(evt)
|
||||
assert.Nil(suite.T(), err, "send event: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
func TestReportStatusFailed(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("failed"))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
// TestReportStatusFailed ...
|
||||
func (suite *HookClientTestSuite) TestReportStatusFailed() {
|
||||
changeData := &job.StatusChange{
|
||||
JobID: "fake_job_ID",
|
||||
JobID: "job_ID_failed",
|
||||
Status: "running",
|
||||
}
|
||||
evt := &Event{
|
||||
URL: ts.URL,
|
||||
URL: suite.mockServer.URL,
|
||||
Data: changeData,
|
||||
Message: fmt.Sprintf("Status of job %s changed to: %s", changeData.JobID, changeData.Status),
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
err := testClient.SendEvent(evt)
|
||||
if err == nil {
|
||||
t.Fatal("expect error but got nil")
|
||||
}
|
||||
err := suite.client.SendEvent(evt)
|
||||
assert.NotNil(suite.T(), err, "send event: expected non nil error but got nil")
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ package job
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/query"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/rds"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
@ -121,8 +120,8 @@ type basicTracker struct {
|
||||
|
||||
// NewBasicTrackerWithID builds a tracker with the provided job ID
|
||||
func NewBasicTrackerWithID(
|
||||
jobID string,
|
||||
ctx context.Context,
|
||||
jobID string,
|
||||
ns string,
|
||||
pool *redis.Pool,
|
||||
callback HookCallback,
|
||||
@ -138,8 +137,8 @@ func NewBasicTrackerWithID(
|
||||
|
||||
// NewBasicTrackerWithStats builds a tracker with the provided job stats
|
||||
func NewBasicTrackerWithStats(
|
||||
stats *Stats,
|
||||
ctx context.Context,
|
||||
stats *Stats,
|
||||
ns string,
|
||||
pool *redis.Pool,
|
||||
callback HookCallback,
|
||||
@ -314,9 +313,13 @@ func (bt *basicTracker) Expire() error {
|
||||
// Run job
|
||||
// Either one is failed, the final return will be marked as failed.
|
||||
func (bt *basicTracker) Run() error {
|
||||
bt.refresh(RunningStatus)
|
||||
err := bt.fireHookEvent(RunningStatus)
|
||||
err = bt.compareAndSet(RunningStatus)
|
||||
err := bt.compareAndSet(RunningStatus)
|
||||
if !errs.IsStatusMismatchError(err) {
|
||||
bt.refresh(RunningStatus)
|
||||
if er := bt.fireHookEvent(RunningStatus); err == nil && er != nil {
|
||||
return er
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@ -325,9 +328,13 @@ func (bt *basicTracker) Run() error {
|
||||
// Stop is final status, if failed to do, retry should be enforced.
|
||||
// Either one is failed, the final return will be marked as failed.
|
||||
func (bt *basicTracker) Stop() error {
|
||||
bt.refresh(StoppedStatus)
|
||||
err := bt.fireHookEvent(StoppedStatus)
|
||||
err = bt.UpdateStatusWithRetry(StoppedStatus)
|
||||
err := bt.UpdateStatusWithRetry(StoppedStatus)
|
||||
if !errs.IsStatusMismatchError(err) {
|
||||
bt.refresh(StoppedStatus)
|
||||
if er := bt.fireHookEvent(StoppedStatus); err == nil && er != nil {
|
||||
return er
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@ -336,9 +343,13 @@ func (bt *basicTracker) Stop() error {
|
||||
// Fail is final status, if failed to do, retry should be enforced.
|
||||
// Either one is failed, the final return will be marked as failed.
|
||||
func (bt *basicTracker) Fail() error {
|
||||
bt.refresh(ErrorStatus)
|
||||
err := bt.fireHookEvent(ErrorStatus)
|
||||
err = bt.UpdateStatusWithRetry(ErrorStatus)
|
||||
err := bt.UpdateStatusWithRetry(ErrorStatus)
|
||||
if !errs.IsStatusMismatchError(err) {
|
||||
bt.refresh(ErrorStatus)
|
||||
if er := bt.fireHookEvent(ErrorStatus); err == nil && er != nil {
|
||||
return er
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@ -347,9 +358,13 @@ func (bt *basicTracker) Fail() error {
|
||||
// Succeed is final status, if failed to do, retry should be enforced.
|
||||
// Either one is failed, the final return will be marked as failed.
|
||||
func (bt *basicTracker) Succeed() error {
|
||||
bt.refresh(SuccessStatus)
|
||||
err := bt.fireHookEvent(SuccessStatus)
|
||||
err = bt.UpdateStatusWithRetry(SuccessStatus)
|
||||
err := bt.UpdateStatusWithRetry(SuccessStatus)
|
||||
if !errs.IsStatusMismatchError(err) {
|
||||
bt.refresh(SuccessStatus)
|
||||
if er := bt.fireHookEvent(SuccessStatus); err == nil && er != nil {
|
||||
return er
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@ -446,12 +461,15 @@ func (bt *basicTracker) Save() (err error) {
|
||||
func (bt *basicTracker) UpdateStatusWithRetry(targetStatus Status) error {
|
||||
err := bt.compareAndSet(targetStatus)
|
||||
if err != nil {
|
||||
// Push to the retrying Q
|
||||
if er := bt.pushToQueueForRetry(targetStatus); er != nil {
|
||||
logger.Errorf("push job status update request to retry queue error: %s", er)
|
||||
// If failed to put it into the retrying Q in case, let's downgrade to retry in current process
|
||||
// by recursively call in goroutines.
|
||||
bt.retryUpdateStatus(targetStatus)
|
||||
// Status mismatching error will be ignored
|
||||
if !errs.IsStatusMismatchError(err) {
|
||||
// Push to the retrying Q
|
||||
if er := bt.pushToQueueForRetry(targetStatus); er != nil {
|
||||
logger.Errorf("push job status update request to retry queue error: %s", er)
|
||||
// If failed to put it into the retrying Q in case, let's downgrade to retry in current process
|
||||
// by recursively call in goroutines.
|
||||
bt.retryUpdateStatus(targetStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -550,7 +568,7 @@ func (bt *basicTracker) compareAndSet(targetStatus Status) error {
|
||||
|
||||
diff := st.Compare(targetStatus)
|
||||
if diff > 0 {
|
||||
return fmt.Errorf("mismatch job status: current %s, setting to %s", st, targetStatus)
|
||||
return errs.StatusMismatchError(st.String(), targetStatus.String())
|
||||
}
|
||||
if diff == 0 {
|
||||
// Desired matches actual
|
||||
|
116
src/jobservice/job/tracker_test.go
Normal file
116
src/jobservice/job/tracker_test.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package job
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TrackerTestSuite tests functions of tracker
|
||||
type TrackerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
tracker *basicTracker
|
||||
namespace string
|
||||
pool *redis.Pool
|
||||
}
|
||||
|
||||
// TestTrackerTestSuite is entry of go test
|
||||
func TestTrackerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(TrackerTestSuite))
|
||||
}
|
||||
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *TrackerTestSuite) SetupSuite() {
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
}
|
||||
|
||||
// SetupTest prepares for test cases
|
||||
func (suite *TrackerTestSuite) SetupTest() {
|
||||
suite.tracker = &basicTracker{
|
||||
namespace: suite.namespace,
|
||||
context: context.Background(),
|
||||
pool: suite.pool,
|
||||
callback: func(hookURL string, change *StatusChange) error { return nil },
|
||||
}
|
||||
}
|
||||
|
||||
// TearDownSuite prepares test suites
|
||||
func (suite *TrackerTestSuite) TearDownSuite() {
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
tests.ClearAll(suite.namespace, conn)
|
||||
}
|
||||
|
||||
// TestTracker tests tracker
|
||||
func (suite *TrackerTestSuite) TestTracker() {
|
||||
jobID := utils.MakeIdentifier()
|
||||
mockJobStats := &Stats{
|
||||
Info: &StatsInfo{
|
||||
JobID: jobID,
|
||||
Status: SuccessStatus.String(),
|
||||
JobKind: KindGeneric,
|
||||
JobName: SampleJob,
|
||||
IsUnique: false,
|
||||
},
|
||||
}
|
||||
|
||||
suite.tracker.jobStats = mockJobStats
|
||||
suite.tracker.jobID = jobID
|
||||
|
||||
err := suite.tracker.Save()
|
||||
require.Nil(suite.T(), err, "save: nil error expected but got %s", err)
|
||||
|
||||
s, err := suite.tracker.Status()
|
||||
assert.Nil(suite.T(), err, "get status: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), SuccessStatus, s, "get status: expected pending but got %s", s)
|
||||
|
||||
j := suite.tracker.Job()
|
||||
assert.Equal(suite.T(), jobID, j.Info.JobID, "job: expect job ID %s but got %s", jobID, j.Info.JobID)
|
||||
|
||||
err = suite.tracker.Update("web_hook_url", "http://hook.url")
|
||||
assert.Nil(suite.T(), err, "update: nil error expected but got %s", err)
|
||||
|
||||
err = suite.tracker.Load()
|
||||
assert.Nil(suite.T(), err, "load: nil error expected but got %s", err)
|
||||
assert.Equal(
|
||||
suite.T(),
|
||||
"http://hook.url",
|
||||
suite.tracker.jobStats.Info.WebHookURL,
|
||||
"web hook: expect %s but got %s",
|
||||
"http://hook.url",
|
||||
suite.tracker.jobStats.Info.WebHookURL,
|
||||
)
|
||||
|
||||
err = suite.tracker.Run()
|
||||
assert.Error(suite.T(), err, "run: non nil error expected but got nil")
|
||||
err = suite.tracker.CheckIn("check in")
|
||||
assert.Nil(suite.T(), err, "check in: nil error expected but got %s", err)
|
||||
err = suite.tracker.Succeed()
|
||||
assert.Nil(suite.T(), err, "succeed: nil error expected but got %s", err)
|
||||
err = suite.tracker.Stop()
|
||||
assert.Nil(suite.T(), err, "stop: nil error expected but got %s", err)
|
||||
err = suite.tracker.Fail()
|
||||
assert.Nil(suite.T(), err, "fail: nil error expected but got %s", err)
|
||||
}
|
@ -84,7 +84,7 @@ func (bc *basicController) New(stats *job.Stats) (job.Tracker, error) {
|
||||
return nil, errors.Errorf("error occurred when creating job tracker: %s", err)
|
||||
}
|
||||
|
||||
bt := job.NewBasicTrackerWithStats(stats, bc.context, bc.namespace, bc.pool, bc.callback)
|
||||
bt := job.NewBasicTrackerWithStats(bc.context, stats, bc.namespace, bc.pool, bc.callback)
|
||||
if err := bt.Save(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -94,7 +94,7 @@ func (bc *basicController) New(stats *job.Stats) (job.Tracker, error) {
|
||||
|
||||
// Track and attache with the job
|
||||
func (bc *basicController) Track(jobID string) (job.Tracker, error) {
|
||||
bt := job.NewBasicTrackerWithID(jobID, bc.context, bc.namespace, bc.pool, bc.callback)
|
||||
bt := job.NewBasicTrackerWithID(bc.context, jobID, bc.namespace, bc.pool, bc.callback)
|
||||
if err := bt.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -118,7 +118,7 @@ func (bc *basicController) loopForRestoreDeadStatus() {
|
||||
|
||||
if err := bc.restoreDeadStatus(); err != nil {
|
||||
waitInterval := shortLoopInterval
|
||||
if err == rds.NoElementsError {
|
||||
if err == rds.ErrNoElements {
|
||||
// No elements
|
||||
waitInterval = longLoopInterval
|
||||
} else {
|
||||
|
@ -15,59 +15,106 @@
|
||||
package lcm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/rds"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ns = tests.GiveMeTestNamespace()
|
||||
pool = tests.GiveMeRedisPool()
|
||||
ctl = NewController(ns, pool)
|
||||
)
|
||||
// LcmControllerTestSuite tests functions of life cycle controller
|
||||
type LcmControllerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
func TestLifeCycleController(t *testing.T) {
|
||||
conn := pool.Get()
|
||||
defer tests.ClearAll(ns, conn)
|
||||
|
||||
// only mock status data
|
||||
jobID := "fake_job_ID_lcm_ctl"
|
||||
key := rds.KeyJobStats(ns, jobID)
|
||||
if err := setStatus(conn, key, job.PendingStatus); err != nil {
|
||||
t.Fatalf("mock data failed: %s\n", err.Error())
|
||||
}
|
||||
|
||||
// Switch status one by one
|
||||
tk := ctl.Track(jobID)
|
||||
|
||||
current, err := tk.Current()
|
||||
nilError(t, err)
|
||||
expect(t, job.PendingStatus, current)
|
||||
|
||||
nilError(t, tk.Run())
|
||||
current, err = tk.Current()
|
||||
nilError(t, err)
|
||||
expect(t, job.RunningStatus, current)
|
||||
|
||||
nilError(t, tk.Succeed())
|
||||
current, err = tk.Current()
|
||||
nilError(t, err)
|
||||
expect(t, job.SuccessStatus, current)
|
||||
|
||||
if err := tk.Fail(); err == nil {
|
||||
t.Fatalf("expect non nil error but got nil when switch status from %s to %s", current, job.ErrorStatus)
|
||||
}
|
||||
namespace string
|
||||
pool *redis.Pool
|
||||
ctl Controller
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func expect(t *testing.T, expected job.Status, current job.Status) {
|
||||
if expected != current {
|
||||
t.Fatalf("expect status %s but got %s", expected, current)
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *LcmControllerTestSuite) SetupSuite() {
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
suite.cancel = cancel
|
||||
context := &env.Context{
|
||||
SystemContext: ctx,
|
||||
WG: new(sync.WaitGroup),
|
||||
}
|
||||
suite.ctl = NewController(context, suite.namespace, suite.pool, func(hookURL string, change *job.StatusChange) error { return nil })
|
||||
}
|
||||
|
||||
func nilError(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// TearDownSuite clears test suite
|
||||
func (suite *LcmControllerTestSuite) TearDownSuite() {
|
||||
suite.cancel()
|
||||
}
|
||||
|
||||
// TestLcmControllerTestSuite is entry of go test
|
||||
func TestLcmControllerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(LcmControllerTestSuite))
|
||||
}
|
||||
|
||||
// TestNewAndTrack tests controller.New() and controller.Track()
|
||||
func (suite *LcmControllerTestSuite) TestNewAndTrack() {
|
||||
jobID := utils.MakeIdentifier()
|
||||
suite.newsStats(jobID)
|
||||
|
||||
t, err := suite.ctl.Track(jobID)
|
||||
require.Nil(suite.T(), err, "lcm track: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), job.SampleJob, t.Job().Info.JobName, "lcm new: expect job name %s but got %s", job.SampleJob, t.Job().Info.JobName)
|
||||
}
|
||||
|
||||
// TestNew tests controller.Serve()
|
||||
func (suite *LcmControllerTestSuite) TestServe() {
|
||||
// Prepare mock data
|
||||
jobID := utils.MakeIdentifier()
|
||||
suite.newsStats(jobID)
|
||||
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
simpleChange := &job.SimpleStatusChange{
|
||||
JobID: jobID,
|
||||
TargetStatus: job.RunningStatus.String(),
|
||||
}
|
||||
rawJSON, err := json.Marshal(simpleChange)
|
||||
require.Nil(suite.T(), err, "json marshal: nil error expected but got %s", err)
|
||||
key := rds.KeyStatusUpdateRetryQueue(suite.namespace)
|
||||
args := []interface{}{key, "NX", time.Now().Unix(), rawJSON}
|
||||
_, err = conn.Do("ZADD", args...)
|
||||
require.Nil(suite.T(), err, "prepare mock data: nil error expected but got %s", err)
|
||||
|
||||
suite.ctl.Serve()
|
||||
<-time.After(1 * time.Second)
|
||||
|
||||
count, err := redis.Int(conn.Do("ZCARD", key))
|
||||
require.Nil(suite.T(), err, "get total dead status: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), 0, count)
|
||||
}
|
||||
|
||||
// newsStats create job stats
|
||||
func (suite *LcmControllerTestSuite) newsStats(jobID string) {
|
||||
stats := &job.Stats{
|
||||
Info: &job.StatsInfo{
|
||||
JobID: jobID,
|
||||
JobKind: job.KindGeneric,
|
||||
JobName: job.SampleJob,
|
||||
IsUnique: true,
|
||||
Status: job.PendingStatus.String(),
|
||||
},
|
||||
}
|
||||
|
||||
t, err := suite.ctl.New(stats)
|
||||
require.Nil(suite.T(), err, "lcm new: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), jobID, t.Job().Info.JobID, "lcm new: expect job ID %s but got %s", jobID, t.Job().Info.JobID)
|
||||
}
|
||||
|
@ -15,91 +15,116 @@ package period
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/lcm"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/opm"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
)
|
||||
|
||||
var redisPool = tests.GiveMeRedisPool()
|
||||
// BasicSchedulerTestSuite tests functions of basic scheduler
|
||||
type BasicSchedulerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
func TestScheduler(t *testing.T) {
|
||||
statsManager := opm.NewRedisJobStatsManager(context.Background(), tests.GiveMeTestNamespace(), redisPool)
|
||||
statsManager.Start()
|
||||
defer statsManager.Shutdown()
|
||||
cancel context.CancelFunc
|
||||
namespace string
|
||||
pool *redis.Pool
|
||||
|
||||
scheduler := myPeriodicScheduler(statsManager)
|
||||
params := make(map[string]interface{})
|
||||
params["image"] = "testing:v1"
|
||||
id, runAt, err := scheduler.Schedule("fake_job", params, "5 * * * * *")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if time.Now().Unix() >= runAt {
|
||||
t.Fatal("the running at time of scheduled job should be after now, but seems not")
|
||||
}
|
||||
|
||||
if err := scheduler.Load(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if scheduler.pstore.size() != 1 {
|
||||
t.Fatalf("expect 1 item in pstore but got '%d'\n", scheduler.pstore.size())
|
||||
}
|
||||
|
||||
if err := scheduler.UnSchedule(id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := scheduler.Clear(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = tests.Clear(utils.KeyPeriodicPolicy(tests.GiveMeTestNamespace()), redisPool.Get())
|
||||
err = tests.Clear(utils.KeyPeriodicPolicyScore(tests.GiveMeTestNamespace()), redisPool.Get())
|
||||
err = tests.Clear(utils.KeyPeriodicNotification(tests.GiveMeTestNamespace()), redisPool.Get())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lcmCtl lcm.Controller
|
||||
scheduler Scheduler
|
||||
}
|
||||
|
||||
func TestPubFunc(t *testing.T) {
|
||||
statsManager := opm.NewRedisJobStatsManager(context.Background(), tests.GiveMeTestNamespace(), redisPool)
|
||||
statsManager.Start()
|
||||
defer statsManager.Shutdown()
|
||||
// SetupSuite prepares the test suite
|
||||
func (suite *BasicSchedulerTestSuite) SetupSuite() {
|
||||
ctx, cancel := context.WithCancel(context.WithValue(context.Background(), utils.NodeID, "fake_node_ID"))
|
||||
suite.cancel = cancel
|
||||
|
||||
scheduler := myPeriodicScheduler(statsManager)
|
||||
p := &PeriodicJobPolicy{
|
||||
PolicyID: "fake_ID",
|
||||
JobName: "fake_job",
|
||||
CronSpec: "5 * * * * *",
|
||||
}
|
||||
if err := scheduler.AcceptPeriodicPolicy(p); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if scheduler.pstore.size() != 1 {
|
||||
t.Fatalf("expect 1 item in pstore but got '%d' after accepting \n", scheduler.pstore.size())
|
||||
}
|
||||
if rmp := scheduler.RemovePeriodicPolicy("fake_ID"); rmp == nil {
|
||||
t.Fatal("expect none nil object returned after removing but got nil")
|
||||
}
|
||||
if scheduler.pstore.size() != 0 {
|
||||
t.Fatalf("expect 0 item in pstore but got '%d' \n", scheduler.pstore.size())
|
||||
}
|
||||
}
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
|
||||
func myPeriodicScheduler(statsManager opm.JobStatsManager) *basicScheduler {
|
||||
sysCtx := context.Background()
|
||||
ctx := &env.Context{
|
||||
SystemContext: sysCtx,
|
||||
envCtx := &env.Context{
|
||||
SystemContext: ctx,
|
||||
WG: new(sync.WaitGroup),
|
||||
ErrorChan: make(chan error, 1),
|
||||
}
|
||||
|
||||
return NewScheduler(ctx, tests.GiveMeTestNamespace(), redisPool, statsManager)
|
||||
suite.lcmCtl = lcm.NewController(
|
||||
envCtx,
|
||||
suite.namespace,
|
||||
suite.pool,
|
||||
func(hookURL string, change *job.StatusChange) error { return nil },
|
||||
)
|
||||
|
||||
suite.scheduler = NewScheduler(ctx, suite.namespace, suite.pool, suite.lcmCtl)
|
||||
}
|
||||
|
||||
// TearDownSuite clears the test suite
|
||||
func (suite *BasicSchedulerTestSuite) TearDownSuite() {
|
||||
suite.cancel()
|
||||
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
tests.ClearAll(suite.namespace, conn)
|
||||
}
|
||||
|
||||
// TestSchedulerTestSuite is entry of go test
|
||||
func TestSchedulerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(BasicSchedulerTestSuite))
|
||||
}
|
||||
|
||||
// TestScheduler tests scheduling and un-scheduling
|
||||
func (suite *BasicSchedulerTestSuite) TestScheduler() {
|
||||
go func() {
|
||||
<-time.After(1 * time.Second)
|
||||
suite.scheduler.Stop()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
var err error
|
||||
defer func() {
|
||||
require.NoError(suite.T(), err, "start scheduler: nil error expected but got %s", err)
|
||||
}()
|
||||
|
||||
err = suite.scheduler.Start()
|
||||
}()
|
||||
|
||||
// Prepare one
|
||||
now := time.Now()
|
||||
minute := now.Minute()
|
||||
coreSpec := fmt.Sprintf("30,50 %d * * * *", minute+2)
|
||||
p := &Policy{
|
||||
ID: "fake_policy",
|
||||
JobName: job.SampleJob,
|
||||
CronSpec: coreSpec,
|
||||
}
|
||||
|
||||
pid, err := suite.scheduler.Schedule(p)
|
||||
require.NoError(suite.T(), err, "schedule: nil error expected but got %s", err)
|
||||
assert.Condition(suite.T(), func() bool {
|
||||
return pid > 0
|
||||
}, "schedule: returned pid should >0")
|
||||
|
||||
jobStats := &job.Stats{
|
||||
Info: &job.StatsInfo{
|
||||
JobID: p.ID,
|
||||
Status: job.ScheduledStatus.String(),
|
||||
JobName: job.SampleJob,
|
||||
JobKind: job.KindPeriodic,
|
||||
NumericPID: pid,
|
||||
CronSpec: coreSpec,
|
||||
},
|
||||
}
|
||||
_, err = suite.lcmCtl.New(jobStats)
|
||||
require.NoError(suite.T(), err, "lcm new: nil error expected but got %s", err)
|
||||
|
||||
err = suite.scheduler.UnSchedule(p.ID)
|
||||
require.NoError(suite.T(), err, "unschedule: nil error expected but got %s", err)
|
||||
}
|
||||
|
@ -187,7 +187,6 @@ func (e *enqueuer) scheduleNextJobs(p *Policy, conn redis.Conn) {
|
||||
// Add extra argument for job running
|
||||
// Notes: Only for system using
|
||||
wJobParams[PeriodicExecutionMark] = true
|
||||
|
||||
for t := schedule.Next(nowTime); t.Before(horizon); t = schedule.Next(t) {
|
||||
epoch := t.Unix()
|
||||
|
||||
@ -288,7 +287,7 @@ func (e *enqueuer) shouldEnqueue() bool {
|
||||
shouldEnq := false
|
||||
lastEnqueue, err := redis.Int64(conn.Do("GET", rds.RedisKeyLastPeriodicEnqueue(e.namespace)))
|
||||
if err != nil {
|
||||
if err != redis.ErrNil {
|
||||
if err.Error() != redis.ErrNil.Error() {
|
||||
// Logged error
|
||||
logger.Errorf("get timestamp of last enqueue error: %s", err)
|
||||
}
|
||||
@ -304,10 +303,11 @@ func (e *enqueuer) shouldEnqueue() bool {
|
||||
// Set last periodic enqueue timestamp
|
||||
if _, err := conn.Do("SET", rds.RedisKeyLastPeriodicEnqueue(e.namespace), time.Now().Unix()); err != nil {
|
||||
logger.Errorf("set last periodic enqueue timestamp error: %s", err)
|
||||
// Anyway the action should be enforced
|
||||
// The negative effect of this failure is just more re-enqueues by other nodes
|
||||
return true
|
||||
}
|
||||
|
||||
// Anyway the action should be enforced
|
||||
// The negative effect of this failure is just more re-enqueues by other nodes
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
|
@ -15,63 +15,114 @@ package period
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/rds"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/lcm"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/opm"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
)
|
||||
|
||||
func TestPeriodicEnqueuerStartStop(t *testing.T) {
|
||||
ns := tests.GiveMeTestNamespace()
|
||||
ps := &periodicJobPolicyStore{
|
||||
lock: new(sync.RWMutex),
|
||||
policies: make(map[string]*PeriodicJobPolicy),
|
||||
}
|
||||
enqueuer := newEnqueuer(ns, redisPool, ps, nil)
|
||||
enqueuer.start()
|
||||
<-time.After(100 * time.Millisecond)
|
||||
enqueuer.stop()
|
||||
// EnqueuerTestSuite tests functions of enqueuer
|
||||
type EnqueuerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
enqueuer *enqueuer
|
||||
namespace string
|
||||
pool *redis.Pool
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func TestEnqueue(t *testing.T) {
|
||||
ns := tests.GiveMeTestNamespace()
|
||||
|
||||
pl := &PeriodicJobPolicy{
|
||||
PolicyID: "fake_ID",
|
||||
JobName: "fake_name",
|
||||
CronSpec: "5 * * * * *",
|
||||
}
|
||||
ps := &periodicJobPolicyStore{
|
||||
lock: new(sync.RWMutex),
|
||||
policies: make(map[string]*PeriodicJobPolicy),
|
||||
}
|
||||
ps.add(pl)
|
||||
|
||||
statsManager := opm.NewRedisJobStatsManager(context.Background(), ns, redisPool)
|
||||
statsManager.Start()
|
||||
defer statsManager.Shutdown()
|
||||
|
||||
enqueuer := newEnqueuer(ns, redisPool, ps, statsManager)
|
||||
if err := enqueuer.enqueue(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := clear(ns); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// TestEnqueuerTestSuite is entry of go test
|
||||
func TestEnqueuerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(EnqueuerTestSuite))
|
||||
}
|
||||
|
||||
func clear(ns string) error {
|
||||
err := tests.Clear(utils.RedisKeyScheduled(ns), redisPool.Get())
|
||||
err = tests.Clear(utils.KeyJobStats(ns, "fake_ID"), redisPool.Get())
|
||||
err = tests.Clear(utils.RedisKeyLastPeriodicEnqueue(ns), redisPool.Get())
|
||||
if err != nil {
|
||||
return err
|
||||
// SetupSuite prepares the test suite
|
||||
func (suite *EnqueuerTestSuite) SetupSuite() {
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.WithValue(context.Background(), utils.NodeID, "fake_node_ID"))
|
||||
suite.cancel = cancel
|
||||
|
||||
envCtx := &env.Context{
|
||||
SystemContext: ctx,
|
||||
WG: new(sync.WaitGroup),
|
||||
}
|
||||
|
||||
return nil
|
||||
lcmCtl := lcm.NewController(
|
||||
envCtx,
|
||||
suite.namespace,
|
||||
suite.pool,
|
||||
func(hookURL string, change *job.StatusChange) error { return nil },
|
||||
)
|
||||
suite.enqueuer = newEnqueuer(ctx, suite.namespace, suite.pool, lcmCtl)
|
||||
|
||||
suite.prepare()
|
||||
}
|
||||
|
||||
// TearDownSuite clears the test suite
|
||||
func (suite *EnqueuerTestSuite) TearDownSuite() {
|
||||
suite.cancel()
|
||||
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
tests.ClearAll(suite.namespace, conn)
|
||||
}
|
||||
|
||||
// TestEnqueuer tests enqueuer
|
||||
func (suite *EnqueuerTestSuite) TestEnqueuer() {
|
||||
go func() {
|
||||
defer func() {
|
||||
suite.enqueuer.stopChan <- true
|
||||
}()
|
||||
|
||||
<-time.After(1 * time.Second)
|
||||
|
||||
key := rds.RedisKeyScheduled(suite.namespace)
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
count, err := redis.Int(conn.Do("ZCARD", key))
|
||||
require.Nil(suite.T(), err, "count scheduled: nil error expected but got %s", err)
|
||||
assert.Condition(suite.T(), func() bool {
|
||||
return count > 0
|
||||
}, "count of scheduled jobs should be greater than 0 but got %d", count)
|
||||
}()
|
||||
|
||||
err := suite.enqueuer.start()
|
||||
require.Nil(suite.T(), err, "enqueuer start: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
func (suite *EnqueuerTestSuite) prepare() {
|
||||
now := time.Now()
|
||||
minute := now.Minute()
|
||||
|
||||
coreSpec := fmt.Sprintf("30,50 %d * * * *", minute+2)
|
||||
|
||||
// Prepare one
|
||||
p := &Policy{
|
||||
ID: "fake_policy",
|
||||
JobName: job.SampleJob,
|
||||
CronSpec: coreSpec,
|
||||
}
|
||||
rawData, err := p.Serialize()
|
||||
assert.Nil(suite.T(), err, "prepare data: nil error expected but got %s", err)
|
||||
key := rds.KeyPeriodicPolicy(suite.namespace)
|
||||
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("ZADD", key, time.Now().Unix(), rawData)
|
||||
assert.Nil(suite.T(), err, "prepare policy: nil error expected but got %s", err)
|
||||
}
|
||||
|
@ -14,67 +14,136 @@
|
||||
package period
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"context"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/rds"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPeriodicJobPolicy(t *testing.T) {
|
||||
p := createPolicy("")
|
||||
// PolicyStoreTestSuite tests functions of policy store
|
||||
type PolicyStoreTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
data, err := p.Serialize()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := p.DeSerialize(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store *policyStore
|
||||
namespace string
|
||||
pool *redis.Pool
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func TestPeriodicJobPolicyStore(t *testing.T) {
|
||||
ps := &periodicJobPolicyStore{
|
||||
lock: new(sync.RWMutex),
|
||||
policies: make(map[string]*PeriodicJobPolicy),
|
||||
}
|
||||
|
||||
ps.add(createPolicy("fake_ID_Steven"))
|
||||
if ps.size() != 1 {
|
||||
t.Errorf("expect size 1 but got '%d'\n", ps.size())
|
||||
}
|
||||
pl := make([]*PeriodicJobPolicy, 0)
|
||||
pl = append(pl, createPolicy(""))
|
||||
pl = append(pl, createPolicy(""))
|
||||
ps.addAll(pl)
|
||||
if ps.size() != 3 {
|
||||
t.Fatalf("expect size 3 but got '%d'\n", ps.size())
|
||||
}
|
||||
|
||||
l := ps.list()
|
||||
if l == nil || len(l) != 3 {
|
||||
t.Fatal("expect a policy list with 3 items but got invalid list")
|
||||
}
|
||||
|
||||
rp := ps.remove("fake_ID_Steven")
|
||||
if rp == nil {
|
||||
t.Fatal("expect none nil policy object but got nil")
|
||||
}
|
||||
// TestPolicyStoreTestSuite is entry of go test
|
||||
func TestPolicyStoreTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(PolicyStoreTestSuite))
|
||||
}
|
||||
|
||||
func createPolicy(id string) *PeriodicJobPolicy {
|
||||
theID := id
|
||||
if theID == "" {
|
||||
theID = fmt.Sprintf("fake_ID_%d", time.Now().UnixNano()+int64(rand.Intn(1000)))
|
||||
}
|
||||
p := &PeriodicJobPolicy{
|
||||
PolicyID: theID,
|
||||
JobName: "fake_job",
|
||||
JobParameters: make(map[string]interface{}),
|
||||
CronSpec: "5 * * * * *",
|
||||
}
|
||||
p.JobParameters["image"] = "testing:v1"
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *PolicyStoreTestSuite) SetupSuite() {
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
suite.cancel = cancel
|
||||
|
||||
return p
|
||||
suite.store = newPolicyStore(ctx, suite.namespace, suite.pool)
|
||||
}
|
||||
|
||||
// TearDownSuite clears the test suite
|
||||
func (suite *PolicyStoreTestSuite) TearDownSuite() {
|
||||
suite.cancel()
|
||||
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
tests.ClearAll(suite.namespace, conn)
|
||||
}
|
||||
|
||||
// TestStore tests policy store serve
|
||||
func (suite *PolicyStoreTestSuite) TestServe() {
|
||||
var err error
|
||||
|
||||
defer func() {
|
||||
suite.store.stopChan <- true
|
||||
assert.Nil(suite.T(), err, "serve exit: nil error expected but got %s", err)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
err = suite.store.serve()
|
||||
}()
|
||||
<-time.After(1 * time.Second)
|
||||
}
|
||||
|
||||
// TestLoad tests load policy from backend
|
||||
func (suite *PolicyStoreTestSuite) TestLoad() {
|
||||
// Prepare one
|
||||
p := &Policy{
|
||||
ID: "fake_policy",
|
||||
JobName: job.SampleJob,
|
||||
CronSpec: "5 * * * * *",
|
||||
}
|
||||
rawData, err := p.Serialize()
|
||||
assert.Nil(suite.T(), err, "prepare data: nil error expected but got %s", err)
|
||||
key := rds.KeyPeriodicPolicy(suite.namespace)
|
||||
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("ZADD", key, time.Now().Unix(), rawData)
|
||||
assert.Nil(suite.T(), err, "add data: nil error expected but got %s", err)
|
||||
|
||||
err = suite.store.load()
|
||||
assert.Nil(suite.T(), err, "load: nil error expected but got %s", err)
|
||||
|
||||
p1 := &Policy{
|
||||
ID: "fake_policy_1",
|
||||
JobName: job.SampleJob,
|
||||
CronSpec: "5 * * * * *",
|
||||
}
|
||||
m := &message{
|
||||
Event: changeEventSchedule,
|
||||
Data: p1,
|
||||
}
|
||||
err = suite.store.sync(m)
|
||||
assert.Nil(suite.T(), err, "sync schedule: nil error expected but got %s", err)
|
||||
|
||||
count := 0
|
||||
suite.store.Iterate(func(id string, p *Policy) bool {
|
||||
count++
|
||||
return true
|
||||
})
|
||||
assert.Equal(suite.T(), 2, count, "expected 2 policies but got %d", count)
|
||||
|
||||
m1 := &message{
|
||||
Event: changeEventUnSchedule,
|
||||
Data: p1,
|
||||
}
|
||||
err = suite.store.sync(m1)
|
||||
assert.Nil(suite.T(), err, "sync unschedule: nil error expected but got %s", err)
|
||||
|
||||
count = 0
|
||||
suite.store.Iterate(func(id string, p *Policy) bool {
|
||||
count++
|
||||
return true
|
||||
})
|
||||
assert.Equal(suite.T(), 1, count, "expected 1 policies but got %d", count)
|
||||
}
|
||||
|
||||
// TestPolicy tests policy itself
|
||||
func (suite *PolicyStoreTestSuite) TestPolicy() {
|
||||
p1 := &Policy{
|
||||
ID: "fake_policy_1",
|
||||
JobName: job.SampleJob,
|
||||
CronSpec: "5 * * * * *",
|
||||
}
|
||||
|
||||
bytes, err := p1.Serialize()
|
||||
assert.Nil(suite.T(), err, "policy serialize: nil error expected but got %s", err)
|
||||
p2 := &Policy{}
|
||||
err = p2.DeSerialize(bytes)
|
||||
assert.Nil(suite.T(), err, "policy deserialize: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), "5 * * * * *", p2.CronSpec)
|
||||
err = p2.Validate()
|
||||
assert.Nil(suite.T(), err, "policy validate: nil error expected but got %s", err)
|
||||
}
|
||||
|
@ -20,41 +20,84 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/logger/backend"
|
||||
"github.com/goharbor/harbor/src/jobservice/models"
|
||||
|
||||
"github.com/gocraft/work"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/config"
|
||||
"github.com/goharbor/harbor/src/jobservice/opm"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/lcm"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestJobWrapper(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
mgr := opm.NewRedisJobStatsManager(ctx, tests.GiveMeTestNamespace(), rPool)
|
||||
mgr.Start()
|
||||
defer mgr.Shutdown()
|
||||
<-time.After(200 * time.Millisecond)
|
||||
// RedisRunnerTestSuite tests functions of redis runner
|
||||
type RedisRunnerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
var launchJobFunc job.LaunchJobFunc = func(req models.JobRequest) (models.JobStats, error) {
|
||||
return models.JobStats{}, nil
|
||||
}
|
||||
ctx = context.WithValue(ctx, utils.CtlKeyOfLaunchJobFunc, launchJobFunc)
|
||||
envContext := &env.Context{
|
||||
lcmCtl lcm.Controller
|
||||
redisJob *RedisJob
|
||||
|
||||
cancel context.CancelFunc
|
||||
namespace string
|
||||
pool *redis.Pool
|
||||
}
|
||||
|
||||
// TestRedisRunnerTestSuite is entry of go test
|
||||
func TestRedisRunnerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(RedisRunnerTestSuite))
|
||||
}
|
||||
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *RedisRunnerTestSuite) SetupSuite() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
suite.cancel = cancel
|
||||
|
||||
envCtx := &env.Context{
|
||||
SystemContext: ctx,
|
||||
WG: &sync.WaitGroup{},
|
||||
ErrorChan: make(chan error, 1), // with 1 buffer
|
||||
WG: new(sync.WaitGroup),
|
||||
ErrorChan: make(chan error, 1),
|
||||
}
|
||||
deDuplicator := NewRedisDeDuplicator(tests.GiveMeTestNamespace(), rPool)
|
||||
wrapper := NewRedisJob((*fakeParentJob)(nil), envContext, mgr, deDuplicator)
|
||||
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
|
||||
suite.lcmCtl = lcm.NewController(
|
||||
envCtx,
|
||||
suite.namespace,
|
||||
suite.pool,
|
||||
func(hookURL string, change *job.StatusChange) error { return nil },
|
||||
)
|
||||
|
||||
fakeStats := &job.Stats{
|
||||
Info: &job.StatsInfo{
|
||||
JobID: "FAKE-j",
|
||||
JobName: "fakeParentJob",
|
||||
JobKind: job.KindGeneric,
|
||||
Status: job.PendingStatus.String(),
|
||||
IsUnique: false,
|
||||
},
|
||||
}
|
||||
_, err := suite.lcmCtl.New(fakeStats)
|
||||
require.NoError(suite.T(), err, "lcm new: nil error expected but got %s", err)
|
||||
|
||||
suite.redisJob = NewRedisJob((*fakeParentJob)(nil), envCtx, suite.lcmCtl)
|
||||
}
|
||||
|
||||
// TearDownSuite clears the test suite
|
||||
func (suite *RedisRunnerTestSuite) TearDownSuite() {
|
||||
suite.cancel()
|
||||
}
|
||||
|
||||
// TestJobWrapper tests the redis job wrapper
|
||||
func (suite *RedisRunnerTestSuite) TestJobWrapper() {
|
||||
j := &work.Job{
|
||||
ID: "FAKE",
|
||||
Name: "DEMO",
|
||||
ID: "FAKE-j",
|
||||
Name: "fakeParentJob",
|
||||
EnqueuedAt: time.Now().Add(5 * time.Minute).Unix(),
|
||||
}
|
||||
|
||||
@ -86,9 +129,8 @@ func TestJobWrapper(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
if err := wrapper.Run(j); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := suite.redisJob.Run(j)
|
||||
require.NoError(suite.T(), err, "redis job: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
type fakeParentJob struct{}
|
||||
@ -101,20 +143,12 @@ func (j *fakeParentJob) ShouldRetry() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (j *fakeParentJob) Validate(params map[string]interface{}) error {
|
||||
func (j *fakeParentJob) Validate(params job.Parameters) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *fakeParentJob) Run(ctx env.JobContext, params map[string]interface{}) error {
|
||||
func (j *fakeParentJob) Run(ctx job.Context, params job.Parameters) error {
|
||||
ctx.Checkin("start")
|
||||
ctx.OPCommand()
|
||||
ctx.LaunchJob(models.JobRequest{
|
||||
Job: &models.JobData{
|
||||
Name: "SUB_JOB",
|
||||
Metadata: &models.JobMetadata{
|
||||
JobKind: job.KindGeneric,
|
||||
},
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
86
src/jobservice/runtime/bootstrap_test.go
Normal file
86
src/jobservice/runtime/bootstrap_test.go
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/config"
|
||||
"github.com/goharbor/harbor/src/jobservice/logger"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BootStrapTestSuite tests bootstrap
|
||||
type BootStrapTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
jobService *Bootstrap
|
||||
cancel context.CancelFunc
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *BootStrapTestSuite) SetupSuite() {
|
||||
// Load configurations
|
||||
err := config.DefaultConfig.Load("../config_test.yml", true)
|
||||
require.NoError(suite.T(), err, "load configurations error: %s", err)
|
||||
|
||||
// Append node ID
|
||||
vCtx := context.WithValue(context.Background(), utils.NodeID, utils.GenerateNodeID())
|
||||
// Create the root context
|
||||
suite.ctx, suite.cancel = context.WithCancel(vCtx)
|
||||
|
||||
// Initialize logger
|
||||
err = logger.Init(suite.ctx)
|
||||
require.NoError(suite.T(), err, "init logger: nil error expected but got %s", err)
|
||||
|
||||
suite.jobService = &Bootstrap{}
|
||||
suite.jobService.SetJobContextInitializer(nil)
|
||||
}
|
||||
|
||||
// TearDownSuite clears the test suite
|
||||
func (suite *BootStrapTestSuite) TearDownSuite() {
|
||||
suite.cancel()
|
||||
|
||||
pool := tests.GiveMeRedisPool()
|
||||
conn := pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
tests.ClearAll(tests.GiveMeTestNamespace(), conn)
|
||||
}
|
||||
|
||||
// TestBootStrapTestSuite is entry of go test
|
||||
func TestBootStrapTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(BootStrapTestSuite))
|
||||
}
|
||||
|
||||
// TestBootStrap tests bootstrap
|
||||
func (suite *BootStrapTestSuite) TestBootStrap() {
|
||||
go func() {
|
||||
var err error
|
||||
defer func() {
|
||||
require.NoError(suite.T(), err, "load and run: nil error expected but got %s", err)
|
||||
}()
|
||||
|
||||
err = suite.jobService.LoadAndRun(suite.ctx, suite.cancel)
|
||||
}()
|
||||
|
||||
<-time.After(1 * time.Second)
|
||||
suite.cancel()
|
||||
}
|
@ -62,7 +62,6 @@ func GiveMeTestNamespace() string {
|
||||
// Clear ...
|
||||
func Clear(key string, conn redis.Conn) error {
|
||||
if conn != nil {
|
||||
defer conn.Close()
|
||||
_, err := conn.Do("DEL", key)
|
||||
return err
|
||||
}
|
||||
@ -72,8 +71,6 @@ func Clear(key string, conn redis.Conn) error {
|
||||
|
||||
// ClearAll ...
|
||||
func ClearAll(namespace string, conn redis.Conn) error {
|
||||
defer conn.Close()
|
||||
|
||||
keys, err := redis.Strings(conn.Do("KEYS", fmt.Sprintf("%s:*", namespace)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
272
src/jobservice/worker/cworker/c_worker_test.go
Normal file
272
src/jobservice/worker/cworker/c_worker_test.go
Normal file
@ -0,0 +1,272 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package cworker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/jobservice/common/utils"
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/lcm"
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
"github.com/goharbor/harbor/src/jobservice/worker"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CWorkerTestSuite tests functions of c worker
|
||||
type CWorkerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
cWorker worker.Interface
|
||||
lcmCtl lcm.Controller
|
||||
|
||||
namespace string
|
||||
pool *redis.Pool
|
||||
|
||||
cancel context.CancelFunc
|
||||
context *env.Context
|
||||
}
|
||||
|
||||
// SetupSuite prepares test suite
|
||||
func (suite *CWorkerTestSuite) SetupSuite() {
|
||||
suite.namespace = tests.GiveMeTestNamespace()
|
||||
suite.pool = tests.GiveMeRedisPool()
|
||||
|
||||
// Append node ID
|
||||
vCtx := context.WithValue(context.Background(), utils.NodeID, utils.GenerateNodeID())
|
||||
// Create the root context
|
||||
ctx, cancel := context.WithCancel(vCtx)
|
||||
suite.cancel = cancel
|
||||
|
||||
envCtx := &env.Context{
|
||||
SystemContext: ctx,
|
||||
WG: new(sync.WaitGroup),
|
||||
ErrorChan: make(chan error, 1),
|
||||
}
|
||||
suite.context = envCtx
|
||||
|
||||
suite.lcmCtl = lcm.NewController(
|
||||
envCtx,
|
||||
suite.namespace,
|
||||
suite.pool,
|
||||
func(hookURL string, change *job.StatusChange) error { return nil },
|
||||
)
|
||||
|
||||
suite.cWorker = NewWorker(envCtx, suite.namespace, 5, suite.pool, suite.lcmCtl)
|
||||
err := suite.cWorker.RegisterJobs(map[string]interface{}{
|
||||
"fake_job": (*fakeJob)(nil),
|
||||
"fake_long_run_job": (*fakeLongRunJob)(nil),
|
||||
})
|
||||
require.NoError(suite.T(), err, "register jobs: nil error expected but got %s", err)
|
||||
|
||||
err = suite.cWorker.Start()
|
||||
require.NoError(suite.T(), err, "start redis worker: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// TearDownSuite clears the test suite
|
||||
func (suite *CWorkerTestSuite) TearDownSuite() {
|
||||
suite.cancel()
|
||||
|
||||
suite.context.WG.Wait()
|
||||
|
||||
conn := suite.pool.Get()
|
||||
defer conn.Close()
|
||||
|
||||
tests.ClearAll(suite.namespace, conn)
|
||||
}
|
||||
|
||||
// TestCWorkerTestSuite is entry fo go test
|
||||
func TestCWorkerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(CWorkerTestSuite))
|
||||
}
|
||||
|
||||
// TestRegisterJobs ...
|
||||
func (suite *CWorkerTestSuite) TestRegisterJobs() {
|
||||
_, ok := suite.cWorker.IsKnownJob("fake_job")
|
||||
assert.EqualValues(suite.T(), true, ok, "expected known job but registering 'fake_job' appears to have failed")
|
||||
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
err := suite.cWorker.ValidateJobParameters((*fakeJob)(nil), params)
|
||||
assert.NoError(suite.T(), err, "validate parameters: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// TestEnqueueJob tests enqueue job
|
||||
func (suite *CWorkerTestSuite) TestEnqueueJob() {
|
||||
params := make(job.Parameters)
|
||||
params["name"] = "testing:v1"
|
||||
|
||||
stats, err := suite.cWorker.Enqueue("fake_job", params, false, "")
|
||||
require.NoError(suite.T(), err, "enqueue job: nil error expected but got %s", err)
|
||||
_, err = suite.lcmCtl.New(stats)
|
||||
assert.NoError(suite.T(), err, "lcm: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// TestEnqueueUniqueJob tests enqueue unique job
|
||||
func (suite *CWorkerTestSuite) TestEnqueueUniqueJob() {
|
||||
params := make(job.Parameters)
|
||||
params["name"] = "testing:v2"
|
||||
|
||||
stats, err := suite.cWorker.Enqueue("fake_job", params, true, "http://fake-hook.com:8080")
|
||||
require.NoError(suite.T(), err, "enqueue unique job: nil error expected but got %s", err)
|
||||
|
||||
_, err = suite.lcmCtl.New(stats)
|
||||
assert.NoError(suite.T(), err, "lcm: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// TestScheduleJob tests schedule job
|
||||
func (suite *CWorkerTestSuite) TestScheduleJob() {
|
||||
params := make(job.Parameters)
|
||||
params["name"] = "testing:v1"
|
||||
|
||||
runAt := time.Now().Unix() + 1
|
||||
stats, err := suite.cWorker.Schedule("fake_job", params, 1, false, "")
|
||||
require.NoError(suite.T(), err, "schedule job: nil error expected but got %s", err)
|
||||
require.Condition(suite.T(), func() bool {
|
||||
return runAt <= stats.Info.RunAt
|
||||
}, "expect returned 'RunAt' should be >= '%d' but seems not", runAt)
|
||||
_, err = suite.lcmCtl.New(stats)
|
||||
assert.NoError(suite.T(), err, "lcm: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// TestEnqueuePeriodicJob tests periodic job
|
||||
func (suite *CWorkerTestSuite) TestEnqueuePeriodicJob() {
|
||||
params := make(job.Parameters)
|
||||
params["name"] = "testing:v1"
|
||||
|
||||
m := time.Now().Minute()
|
||||
_, err := suite.cWorker.PeriodicallyEnqueue(
|
||||
"fake_job",
|
||||
params,
|
||||
fmt.Sprintf("10 %d * * * *", m+2),
|
||||
false,
|
||||
"http://fake-hook.com:8080",
|
||||
)
|
||||
|
||||
require.NoError(suite.T(), err, "periodic job: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// TestWorkerStats tests worker stats
|
||||
func (suite *CWorkerTestSuite) TestWorkerStats() {
|
||||
stats, err := suite.cWorker.Stats()
|
||||
require.NoError(suite.T(), err, "worker stats: nil error expected but got %s", err)
|
||||
assert.Equal(suite.T(), 1, len(stats.Pools), "expected 1 pool but got 0")
|
||||
}
|
||||
|
||||
// TestStopJob test stop job
|
||||
func (suite *CWorkerTestSuite) TestStopJob() {
|
||||
// Stop generic job
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
|
||||
genericJob, err := suite.cWorker.Enqueue("fake_long_run_job", params, false, "")
|
||||
require.NoError(suite.T(), err, "enqueue job: nil error expected but got %s", err)
|
||||
t, err := suite.lcmCtl.New(genericJob)
|
||||
require.NoError(suite.T(), err, "new job stats: nil error expected but got %s", err)
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
latest, err := t.Status()
|
||||
require.NoError(suite.T(), err, "get latest status: nil error expected but got %s", err)
|
||||
assert.EqualValues(suite.T(), job.RunningStatus, latest, "expect job is running now")
|
||||
|
||||
err = suite.cWorker.StopJob(genericJob.Info.JobID)
|
||||
require.NoError(suite.T(), err, "stop job: nil error expected but got %s", err)
|
||||
|
||||
// Stop scheduled job
|
||||
scheduledJob, err := suite.cWorker.Schedule("fake_long_run_job", params, 120, false, "")
|
||||
require.NoError(suite.T(), err, "schedule job: nil error expected but got %s", err)
|
||||
t, err = suite.lcmCtl.New(scheduledJob)
|
||||
require.NoError(suite.T(), err, "new job stats: nil error expected but got %s", err)
|
||||
|
||||
err = suite.cWorker.StopJob(scheduledJob.Info.JobID)
|
||||
require.NoError(suite.T(), err, "stop job: nil error expected but got %s", err)
|
||||
}
|
||||
|
||||
// TestScheduledJobs tests get scheduled job
|
||||
func (suite *CWorkerTestSuite) TestScheduledJobs() {
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
|
||||
_, total, err := suite.cWorker.ScheduledJobs(nil)
|
||||
require.NoError(suite.T(), err, "get scheduled job: nil error expected but got %s", err)
|
||||
assert.EqualValues(suite.T(), int64(2), total, "expect 1 item but got 0")
|
||||
}
|
||||
|
||||
type fakeJob struct{}
|
||||
|
||||
func (j *fakeJob) MaxFails() uint {
|
||||
return 3
|
||||
}
|
||||
|
||||
func (j *fakeJob) ShouldRetry() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (j *fakeJob) Validate(params job.Parameters) error {
|
||||
if p, ok := params["name"]; ok {
|
||||
if p == "testing:v1" || p == "testing:v2" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("validate: testing error")
|
||||
}
|
||||
|
||||
func (j *fakeJob) Run(ctx job.Context, params job.Parameters) error {
|
||||
ctx.OPCommand()
|
||||
ctx.Checkin("done")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeLongRunJob struct{}
|
||||
|
||||
func (j *fakeLongRunJob) MaxFails() uint {
|
||||
return 3
|
||||
}
|
||||
|
||||
func (j *fakeLongRunJob) ShouldRetry() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (j *fakeLongRunJob) Validate(params job.Parameters) error {
|
||||
if p, ok := params["name"]; ok {
|
||||
if p == "testing:v1" || p == "testing:v2" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("validate: testing error")
|
||||
}
|
||||
|
||||
func (j *fakeLongRunJob) Run(ctx job.Context, params job.Parameters) error {
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if _, stopped := ctx.OPCommand(); stopped {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.Checkin("done")
|
||||
|
||||
return nil
|
||||
}
|
@ -12,7 +12,7 @@ func TestDeDuplicator(t *testing.T) {
|
||||
"image": "ubuntu:latest",
|
||||
}
|
||||
|
||||
rdd := NewDeDuplicator(tests.GiveMeTestNamespace(), rPool)
|
||||
rdd := NewDeDuplicator(tests.GiveMeTestNamespace(), tests.GiveMeRedisPool())
|
||||
|
||||
if err := rdd.MustUnique(jobName, jobParams); err != nil {
|
||||
t.Error(err)
|
||||
|
@ -1,567 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package cworker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/errs"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/logger"
|
||||
"github.com/goharbor/harbor/src/jobservice/models"
|
||||
"github.com/goharbor/harbor/src/jobservice/opm"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/tests"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/env"
|
||||
)
|
||||
|
||||
var rPool = tests.GiveMeRedisPool()
|
||||
|
||||
func TestRegisterJob(t *testing.T) {
|
||||
wp, _, _ := createRedisWorkerPool()
|
||||
defer func() {
|
||||
if err := tests.ClearAll(tests.GiveMeTestNamespace(), redisPool.Get()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := wp.RegisterJob("fake_job", (*fakeJob)(nil)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if _, ok := wp.IsKnownJob("fake_job"); !ok {
|
||||
t.Error("expected known job but registering 'fake_job' appears to have failed")
|
||||
}
|
||||
|
||||
delete(wp.knownJobs, "fake_job")
|
||||
|
||||
jobs := make(map[string]interface{})
|
||||
jobs["fake_job_1st"] = (*fakeJob)(nil)
|
||||
if err := wp.RegisterJobs(jobs); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
if err := wp.ValidateJobParameters((*fakeJob)(nil), params); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnqueueJob(t *testing.T) {
|
||||
wp, sysCtx, cancel := createRedisWorkerPool()
|
||||
defer func() {
|
||||
if err := tests.ClearAll(tests.GiveMeTestNamespace(), redisPool.Get()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
if err := wp.RegisterJob("fake_job", (*fakeJob)(nil)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := wp.RegisterJob("fake_unique_job", (*fakeUniqueJob)(nil)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
go wp.Start()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
stats, err := wp.Enqueue("fake_job", params, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if stats.Stats.JobID == "" {
|
||||
t.Error("expect none nil job stats but got nil")
|
||||
}
|
||||
|
||||
runAt := time.Now().Unix() + 20
|
||||
stats, err = wp.Schedule("fake_job", params, 20, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if stats.Stats.RunAt > 0 && stats.Stats.RunAt < runAt {
|
||||
t.Errorf("expect returned 'RunAt' should be >= '%d' but seems not", runAt)
|
||||
}
|
||||
|
||||
stats, err = wp.Enqueue("fake_unique_job", params, true)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if stats.Stats.JobID == "" {
|
||||
t.Error("expect none nil job stats but got nil")
|
||||
}
|
||||
|
||||
cancel()
|
||||
sysCtx.WG.Wait()
|
||||
}
|
||||
|
||||
func TestEnqueuePeriodicJob(t *testing.T) {
|
||||
wp, _, cancel := createRedisWorkerPool()
|
||||
defer func() {
|
||||
if err := tests.ClearAll(tests.GiveMeTestNamespace(), redisPool.Get()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
if err := wp.RegisterJob("fake_job", (*fakeJob)(nil)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
go wp.Start()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
jobStats, err := wp.PeriodicallyEnqueue("fake_job", params, "10 * * * * *")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
<-time.After(1 * time.Second)
|
||||
|
||||
jStats, err := wp.GetJobStats(jobStats.Stats.JobID)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if jobStats.Stats.JobName != jStats.Stats.JobName {
|
||||
t.Error("expect same job stats but got different ones")
|
||||
}
|
||||
|
||||
if err := wp.StopJob(jStats.Stats.JobID); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// cancel()
|
||||
// <-time.After(1 * time.Second)
|
||||
}
|
||||
|
||||
func TestPoolStats(t *testing.T) {
|
||||
wp, _, cancel := createRedisWorkerPool()
|
||||
defer func() {
|
||||
if err := tests.ClearAll(tests.GiveMeTestNamespace(), redisPool.Get()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
go wp.Start()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
_, err := wp.Stats()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopJob(t *testing.T) {
|
||||
wp, _, cancel := createRedisWorkerPool()
|
||||
defer func() {
|
||||
if err := tests.ClearAll(tests.GiveMeTestNamespace(), redisPool.Get()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
if err := wp.RegisterJob("fake_long_run_job", (*fakeRunnableJob)(nil)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
go wp.Start()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Stop generic job
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
|
||||
genericJob, err := wp.Enqueue("fake_long_run_job", params, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
stats, err := wp.GetJobStats(genericJob.Stats.JobID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stats.Stats.Status != job.RunningStatus {
|
||||
t.Fatalf("expect job running but got %s", stats.Stats.Status)
|
||||
}
|
||||
if err := wp.StopJob(genericJob.Stats.JobID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Stop scheduled job
|
||||
scheduledJob, err := wp.Schedule("fake_long_run_job", params, 120, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
if err := wp.StopJob(scheduledJob.Stats.JobID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelJob(t *testing.T) {
|
||||
wp, _, cancel := createRedisWorkerPool()
|
||||
defer func() {
|
||||
if err := tests.ClearAll(tests.GiveMeTestNamespace(), redisPool.Get()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
if err := wp.RegisterJob("fake_long_run_job", (*fakeRunnableJob)(nil)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
go wp.Start()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Cancel job
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
|
||||
genericJob, err := wp.Enqueue("fake_long_run_job", params, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
stats, err := wp.GetJobStats(genericJob.Stats.JobID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stats.Stats.Status != job.RunningStatus {
|
||||
t.Fatalf("expect job running but got %s", stats.Stats.Status)
|
||||
}
|
||||
|
||||
if err := wp.CancelJob(genericJob.Stats.JobID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
stats, err = wp.GetJobStats(genericJob.Stats.JobID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stats.Stats.Status != job.JobStatusCancelled {
|
||||
t.Fatalf("expect job cancelled but got %s", stats.Stats.Status)
|
||||
}
|
||||
|
||||
if err := wp.RetryJob(genericJob.Stats.JobID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
/*func TestCancelAndRetryJobWithHook(t *testing.T) {
|
||||
wp, _, cancel := createRedisWorkerPool()
|
||||
defer func() {
|
||||
if err := tests.ClearAll(tests.GiveMeTestNamespace(), redisPool.Get()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
if err := wp.RegisterJob("fake_runnable_job", (*fakeRunnableJob)(nil)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go wp.Start()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "ok")
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
params := make(map[string]interface{})
|
||||
params["name"] = "testing:v1"
|
||||
res, err := wp.Enqueue("fake_runnable_job", params, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := wp.RegisterHook(res.Info.JobID, ts.URL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// make sure it's running
|
||||
timer := time.NewTimer(1 * time.Second)
|
||||
defer timer.Stop()
|
||||
|
||||
CHECK:
|
||||
<-timer.C
|
||||
if check, err := wp.GetJobStats(res.Info.JobID); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if check.Info.Status != job.RunningStatus {
|
||||
timer.Reset(1 * time.Second)
|
||||
goto CHECK
|
||||
}
|
||||
}
|
||||
|
||||
// cancel
|
||||
if err := wp.CancelJob(res.Info.JobID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
<-time.After(5 * time.Second)
|
||||
updatedRes, err := wp.GetJobStats(res.Info.JobID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if updatedRes.Info.Status != job.JobStatusCancelled {
|
||||
t.Fatalf("expect job staus '%s' but got '%s'\n", job.JobStatusCancelled, updatedRes.Info.Status)
|
||||
}
|
||||
if updatedRes.Info.DieAt == 0 {
|
||||
t.Fatalf("expect none zero 'DieAt' but got 0 value")
|
||||
}
|
||||
|
||||
// retry
|
||||
if err := wp.RetryJob(updatedRes.Info.JobID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}*/
|
||||
|
||||
func createRedisWorkerPool() (*worker, *env.Context, context.CancelFunc) {
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
envCtx := &env.Context{
|
||||
SystemContext: ctx,
|
||||
WG: new(sync.WaitGroup),
|
||||
ErrorChan: make(chan error, 1),
|
||||
JobContext: newContext(ctx),
|
||||
}
|
||||
|
||||
return NewWorker(envCtx, tests.GiveMeTestNamespace(), 3, rPool), envCtx, cancel
|
||||
}
|
||||
|
||||
type fakeJob struct{}
|
||||
|
||||
func (j *fakeJob) MaxFails() uint {
|
||||
return 3
|
||||
}
|
||||
|
||||
func (j *fakeJob) ShouldRetry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (j *fakeJob) Validate(params map[string]interface{}) error {
|
||||
if p, ok := params["name"]; ok {
|
||||
if p == "testing:v1" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("testing error")
|
||||
}
|
||||
|
||||
func (j *fakeJob) Run(ctx env.JobContext, params map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeUniqueJob struct{}
|
||||
|
||||
func (j *fakeUniqueJob) MaxFails() uint {
|
||||
return 3
|
||||
}
|
||||
|
||||
func (j *fakeUniqueJob) ShouldRetry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (j *fakeUniqueJob) Validate(params map[string]interface{}) error {
|
||||
if p, ok := params["name"]; ok {
|
||||
if p == "testing:v1" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("testing error")
|
||||
}
|
||||
|
||||
func (j *fakeUniqueJob) Run(ctx env.JobContext, params map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeRunnableJob struct{}
|
||||
|
||||
func (j *fakeRunnableJob) MaxFails() uint {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (j *fakeRunnableJob) ShouldRetry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (j *fakeRunnableJob) Validate(params map[string]interface{}) error {
|
||||
if p, ok := params["name"]; ok {
|
||||
if p == "testing:v1" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("testing error")
|
||||
}
|
||||
|
||||
func (j *fakeRunnableJob) Run(ctx env.JobContext, params map[string]interface{}) error {
|
||||
tk := time.NewTicker(200 * time.Millisecond)
|
||||
defer tk.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tk.C:
|
||||
cmd, ok := ctx.OPCommand()
|
||||
if ok {
|
||||
if cmd == opm.CtlCommandStop {
|
||||
return errs.JobStoppedError()
|
||||
}
|
||||
|
||||
return errs.JobCancelledError()
|
||||
}
|
||||
case <-ctx.SystemContext().Done():
|
||||
return nil
|
||||
case <-time.After(1 * time.Minute):
|
||||
return errors.New("fake job timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fakeContext struct {
|
||||
// System context
|
||||
sysContext context.Context
|
||||
|
||||
// op command func
|
||||
opCommandFunc job.CheckOPCmdFunc
|
||||
|
||||
// checkin func
|
||||
checkInFunc job.CheckInFunc
|
||||
|
||||
// launch job
|
||||
launchJobFunc job.LaunchJobFunc
|
||||
|
||||
// other required information
|
||||
properties map[string]interface{}
|
||||
}
|
||||
|
||||
func newContext(sysCtx context.Context) *fakeContext {
|
||||
return &fakeContext{
|
||||
sysContext: sysCtx,
|
||||
properties: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Build implements the same method in env.Context interface
|
||||
// This func will build the job execution context before running
|
||||
func (c *fakeContext) Build(dep env.JobData) (env.JobContext, error) {
|
||||
jContext := &fakeContext{
|
||||
sysContext: c.sysContext,
|
||||
properties: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Copy properties
|
||||
if len(c.properties) > 0 {
|
||||
for k, v := range c.properties {
|
||||
jContext.properties[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if opCommandFunc, ok := dep.ExtraData["opCommandFunc"]; ok {
|
||||
if reflect.TypeOf(opCommandFunc).Kind() == reflect.Func {
|
||||
if funcRef, ok := opCommandFunc.(job.CheckOPCmdFunc); ok {
|
||||
jContext.opCommandFunc = funcRef
|
||||
}
|
||||
}
|
||||
}
|
||||
if jContext.opCommandFunc == nil {
|
||||
return nil, errors.New("failed to inject opCommandFunc")
|
||||
}
|
||||
|
||||
if checkInFunc, ok := dep.ExtraData["checkInFunc"]; ok {
|
||||
if reflect.TypeOf(checkInFunc).Kind() == reflect.Func {
|
||||
if funcRef, ok := checkInFunc.(job.CheckInFunc); ok {
|
||||
jContext.checkInFunc = funcRef
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if jContext.checkInFunc == nil {
|
||||
return nil, errors.New("failed to inject checkInFunc")
|
||||
}
|
||||
|
||||
if launchJobFunc, ok := dep.ExtraData["launchJobFunc"]; ok {
|
||||
if reflect.TypeOf(launchJobFunc).Kind() == reflect.Func {
|
||||
if funcRef, ok := launchJobFunc.(job.LaunchJobFunc); ok {
|
||||
jContext.launchJobFunc = funcRef
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if jContext.launchJobFunc == nil {
|
||||
return nil, errors.New("failed to inject launchJobFunc")
|
||||
}
|
||||
|
||||
return jContext, nil
|
||||
}
|
||||
|
||||
// Get implements the same method in env.Context interface
|
||||
func (c *fakeContext) Get(prop string) (interface{}, bool) {
|
||||
v, ok := c.properties[prop]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SystemContext implements the same method in env.Context interface
|
||||
func (c *fakeContext) SystemContext() context.Context {
|
||||
return c.sysContext
|
||||
}
|
||||
|
||||
// Checkin is bridge func for reporting detailed status
|
||||
func (c *fakeContext) Checkin(status string) error {
|
||||
if c.checkInFunc != nil {
|
||||
c.checkInFunc(status)
|
||||
} else {
|
||||
return errors.New("nil check in function")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OPCommand return the control operational command like stop/cancel if have
|
||||
func (c *fakeContext) OPCommand() (string, bool) {
|
||||
if c.opCommandFunc != nil {
|
||||
return c.opCommandFunc()
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// GetLogger returns the logger
|
||||
func (c *fakeContext) GetLogger() logger.Interface {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LaunchJob launches sub jobs
|
||||
func (c *fakeContext) LaunchJob(req models.JobRequest) (models.JobStats, error) {
|
||||
if c.launchJobFunc == nil {
|
||||
return models.JobStats{}, errors.New("nil launch job function")
|
||||
}
|
||||
|
||||
return c.launchJobFunc(req)
|
||||
}
|
13
src/vendor/github.com/stretchr/objx/.codeclimate.yml
generated
vendored
Normal file
13
src/vendor/github.com/stretchr/objx/.codeclimate.yml
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
engines:
|
||||
gofmt:
|
||||
enabled: true
|
||||
golint:
|
||||
enabled: true
|
||||
govet:
|
||||
enabled: true
|
||||
|
||||
exclude_patterns:
|
||||
- ".github/"
|
||||
- "vendor/"
|
||||
- "codegen/"
|
||||
- "doc.go"
|
11
src/vendor/github.com/stretchr/objx/.gitignore
generated
vendored
Normal file
11
src/vendor/github.com/stretchr/objx/.gitignore
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
25
src/vendor/github.com/stretchr/objx/.travis.yml
generated
vendored
Normal file
25
src/vendor/github.com/stretchr/objx/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.8
|
||||
- 1.9
|
||||
- tip
|
||||
|
||||
env:
|
||||
global:
|
||||
- CC_TEST_REPORTER_ID=68feaa3410049ce73e145287acbcdacc525087a30627f96f04e579e75bd71c00
|
||||
|
||||
before_script:
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
- ./cc-test-reporter before-build
|
||||
|
||||
install:
|
||||
- go get github.com/go-task/task/cmd/task
|
||||
|
||||
script:
|
||||
- task dl-deps
|
||||
- task lint
|
||||
- task test-coverage
|
||||
|
||||
after_script:
|
||||
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
30
src/vendor/github.com/stretchr/objx/Gopkg.lock
generated
vendored
Normal file
30
src/vendor/github.com/stretchr/objx/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"require"
|
||||
]
|
||||
revision = "b91bfb9ebec76498946beb6af7c0230c7cc7ba6c"
|
||||
version = "v1.2.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "2d160a7dea4ffd13c6c31dab40373822f9d78c73beba016d662bef8f7a998876"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
8
src/vendor/github.com/stretchr/objx/Gopkg.toml
generated
vendored
Normal file
8
src/vendor/github.com/stretchr/objx/Gopkg.toml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
[prune]
|
||||
unused-packages = true
|
||||
non-go = true
|
||||
go-tests = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "~1.2.0"
|
22
src/vendor/github.com/stretchr/objx/LICENSE
generated
vendored
Normal file
22
src/vendor/github.com/stretchr/objx/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2014 Stretchr, Inc.
|
||||
Copyright (c) 2017-2018 objx contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
80
src/vendor/github.com/stretchr/objx/README.md
generated
vendored
Normal file
80
src/vendor/github.com/stretchr/objx/README.md
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
# Objx
|
||||
[![Build Status](https://travis-ci.org/stretchr/objx.svg?branch=master)](https://travis-ci.org/stretchr/objx)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/objx)](https://goreportcard.com/report/github.com/stretchr/objx)
|
||||
[![Maintainability](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/maintainability)](https://codeclimate.com/github/stretchr/objx/maintainability)
|
||||
[![Test Coverage](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/test_coverage)](https://codeclimate.com/github/stretchr/objx/test_coverage)
|
||||
[![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx)
|
||||
[![GoDoc](https://godoc.org/github.com/stretchr/objx?status.svg)](https://godoc.org/github.com/stretchr/objx)
|
||||
|
||||
Objx - Go package for dealing with maps, slices, JSON and other data.
|
||||
|
||||
Get started:
|
||||
|
||||
- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date)
|
||||
- Check out the API Documentation http://godoc.org/github.com/stretchr/objx
|
||||
|
||||
## Overview
|
||||
Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc.
|
||||
|
||||
### Pattern
|
||||
Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going:
|
||||
|
||||
m, err := objx.FromJSON(json)
|
||||
|
||||
NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking.
|
||||
|
||||
Use `Get` to access the value you're interested in. You can use dot and array
|
||||
notation too:
|
||||
|
||||
m.Get("places[0].latlng")
|
||||
|
||||
Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type.
|
||||
|
||||
if m.Get("code").IsStr() { // Your code... }
|
||||
|
||||
Or you can just assume the type, and use one of the strong type methods to extract the real value:
|
||||
|
||||
m.Get("code").Int()
|
||||
|
||||
If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value.
|
||||
|
||||
Get("code").Int(-1)
|
||||
|
||||
If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below.
|
||||
|
||||
### Reading data
|
||||
A simple example of how to use Objx:
|
||||
|
||||
// Use MustFromJSON to make an objx.Map from some JSON
|
||||
m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
|
||||
|
||||
// Get the details
|
||||
name := m.Get("name").Str()
|
||||
age := m.Get("age").Int()
|
||||
|
||||
// Get their nickname (or use their name if they don't have one)
|
||||
nickname := m.Get("nickname").Str(name)
|
||||
|
||||
### Ranging
|
||||
Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect:
|
||||
|
||||
m := objx.MustFromJSON(json)
|
||||
for key, value := range m {
|
||||
// Your code...
|
||||
}
|
||||
|
||||
## Installation
|
||||
To install Objx, use go get:
|
||||
|
||||
go get github.com/stretchr/objx
|
||||
|
||||
### Staying up to date
|
||||
To update Objx to the latest version, run:
|
||||
|
||||
go get -u github.com/stretchr/objx
|
||||
|
||||
### Supported go versions
|
||||
We support the lastest two major Go versions, which are 1.8 and 1.9 at the moment.
|
||||
|
||||
## Contributing
|
||||
Please feel free to submit issues, fork the repository and send pull requests!
|
32
src/vendor/github.com/stretchr/objx/Taskfile.yml
generated
vendored
Normal file
32
src/vendor/github.com/stretchr/objx/Taskfile.yml
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
default:
|
||||
deps: [test]
|
||||
|
||||
dl-deps:
|
||||
desc: Downloads cli dependencies
|
||||
cmds:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
- go get -u github.com/golang/dep/cmd/dep
|
||||
|
||||
update-deps:
|
||||
desc: Updates dependencies
|
||||
cmds:
|
||||
- dep ensure
|
||||
- dep ensure -update
|
||||
|
||||
lint:
|
||||
desc: Runs golint
|
||||
cmds:
|
||||
- go fmt $(go list ./... | grep -v /vendor/)
|
||||
- go vet $(go list ./... | grep -v /vendor/)
|
||||
- golint $(ls *.go | grep -v "doc.go")
|
||||
silent: true
|
||||
|
||||
test:
|
||||
desc: Runs go tests
|
||||
cmds:
|
||||
- go test -race .
|
||||
|
||||
test-coverage:
|
||||
desc: Runs go tests and calucates test coverage
|
||||
cmds:
|
||||
- go test -coverprofile=c.out .
|
148
src/vendor/github.com/stretchr/objx/accessors.go
generated
vendored
Normal file
148
src/vendor/github.com/stretchr/objx/accessors.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// arrayAccesRegexString is the regex used to extract the array number
|
||||
// from the access path
|
||||
const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
|
||||
|
||||
// arrayAccesRegex is the compiled arrayAccesRegexString
|
||||
var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
|
||||
|
||||
// Get gets the value using the specified selector and
|
||||
// returns it inside a new Obj object.
|
||||
//
|
||||
// If it cannot find the value, Get will return a nil
|
||||
// value inside an instance of Obj.
|
||||
//
|
||||
// Get can only operate directly on map[string]interface{} and []interface.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To access the title of the third chapter of the second book, do:
|
||||
//
|
||||
// o.Get("books[1].chapters[2].title")
|
||||
func (m Map) Get(selector string) *Value {
|
||||
rawObj := access(m, selector, nil, false)
|
||||
return &Value{data: rawObj}
|
||||
}
|
||||
|
||||
// Set sets the value using the specified selector and
|
||||
// returns the object on which Set was called.
|
||||
//
|
||||
// Set can only operate directly on map[string]interface{} and []interface
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To set the title of the third chapter of the second book, do:
|
||||
//
|
||||
// o.Set("books[1].chapters[2].title","Time to Go")
|
||||
func (m Map) Set(selector string, value interface{}) Map {
|
||||
access(m, selector, value, true)
|
||||
return m
|
||||
}
|
||||
|
||||
// access accesses the object using the selector and performs the
|
||||
// appropriate action.
|
||||
func access(current, selector, value interface{}, isSet bool) interface{} {
|
||||
switch selector.(type) {
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
if array, ok := current.([]interface{}); ok {
|
||||
index := intFromInterface(selector)
|
||||
if index >= len(array) {
|
||||
return nil
|
||||
}
|
||||
return array[index]
|
||||
}
|
||||
return nil
|
||||
|
||||
case string:
|
||||
selStr := selector.(string)
|
||||
selSegs := strings.SplitN(selStr, PathSeparator, 2)
|
||||
thisSel := selSegs[0]
|
||||
index := -1
|
||||
var err error
|
||||
|
||||
if strings.Contains(thisSel, "[") {
|
||||
arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel)
|
||||
if len(arrayMatches) > 0 {
|
||||
// Get the key into the map
|
||||
thisSel = arrayMatches[1]
|
||||
|
||||
// Get the index into the array at the key
|
||||
index, err = strconv.Atoi(arrayMatches[2])
|
||||
|
||||
if err != nil {
|
||||
// This should never happen. If it does, something has gone
|
||||
// seriously wrong. Panic.
|
||||
panic("objx: Array index is not an integer. Must use array[int].")
|
||||
}
|
||||
}
|
||||
}
|
||||
if curMap, ok := current.(Map); ok {
|
||||
current = map[string]interface{}(curMap)
|
||||
}
|
||||
// get the object in question
|
||||
switch current.(type) {
|
||||
case map[string]interface{}:
|
||||
curMSI := current.(map[string]interface{})
|
||||
if len(selSegs) <= 1 && isSet {
|
||||
curMSI[thisSel] = value
|
||||
return nil
|
||||
}
|
||||
current = curMSI[thisSel]
|
||||
default:
|
||||
current = nil
|
||||
}
|
||||
// do we need to access the item of an array?
|
||||
if index > -1 {
|
||||
if array, ok := current.([]interface{}); ok {
|
||||
if index < len(array) {
|
||||
current = array[index]
|
||||
} else {
|
||||
current = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(selSegs) > 1 {
|
||||
current = access(current, selSegs[1], value, isSet)
|
||||
}
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
// intFromInterface converts an interface object to the largest
|
||||
// representation of an unsigned integer using a type switch and
|
||||
// assertions
|
||||
func intFromInterface(selector interface{}) int {
|
||||
var value int
|
||||
switch selector.(type) {
|
||||
case int:
|
||||
value = selector.(int)
|
||||
case int8:
|
||||
value = int(selector.(int8))
|
||||
case int16:
|
||||
value = int(selector.(int16))
|
||||
case int32:
|
||||
value = int(selector.(int32))
|
||||
case int64:
|
||||
value = int(selector.(int64))
|
||||
case uint:
|
||||
value = int(selector.(uint))
|
||||
case uint8:
|
||||
value = int(selector.(uint8))
|
||||
case uint16:
|
||||
value = int(selector.(uint16))
|
||||
case uint32:
|
||||
value = int(selector.(uint32))
|
||||
case uint64:
|
||||
value = int(selector.(uint64))
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
return value
|
||||
}
|
13
src/vendor/github.com/stretchr/objx/constants.go
generated
vendored
Normal file
13
src/vendor/github.com/stretchr/objx/constants.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package objx
|
||||
|
||||
const (
|
||||
// PathSeparator is the character used to separate the elements
|
||||
// of the keypath.
|
||||
//
|
||||
// For example, `location.address.city`
|
||||
PathSeparator string = "."
|
||||
|
||||
// SignatureSeparator is the character that is used to
|
||||
// separate the Base64 string from the security signature.
|
||||
SignatureSeparator = "_"
|
||||
)
|
108
src/vendor/github.com/stretchr/objx/conversions.go
generated
vendored
Normal file
108
src/vendor/github.com/stretchr/objx/conversions.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// JSON converts the contained object to a JSON string
|
||||
// representation
|
||||
func (m Map) JSON() (string, error) {
|
||||
result, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
err = errors.New("objx: JSON encode failed with: " + err.Error())
|
||||
}
|
||||
return string(result), err
|
||||
}
|
||||
|
||||
// MustJSON converts the contained object to a JSON string
|
||||
// representation and panics if there is an error
|
||||
func (m Map) MustJSON() string {
|
||||
result, err := m.JSON()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Base64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation
|
||||
func (m Map) Base64() (string, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
jsonData, err := m.JSON()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
|
||||
_, err = encoder.Write([]byte(jsonData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = encoder.Close()
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// MustBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and panics
|
||||
// if there is an error
|
||||
func (m Map) MustBase64() string {
|
||||
result, err := m.Base64()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SignedBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and signs it
|
||||
// using the provided key.
|
||||
func (m Map) SignedBase64(key string) (string, error) {
|
||||
base64, err := m.Base64()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sig := HashWithKey(base64, key)
|
||||
return base64 + SignatureSeparator + sig, nil
|
||||
}
|
||||
|
||||
// MustSignedBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and signs it
|
||||
// using the provided key and panics if there is an error
|
||||
func (m Map) MustSignedBase64(key string) string {
|
||||
result, err := m.SignedBase64(key)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
URL Query
|
||||
------------------------------------------------
|
||||
*/
|
||||
|
||||
// URLValues creates a url.Values object from an Obj. This
|
||||
// function requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) URLValues() url.Values {
|
||||
vals := make(url.Values)
|
||||
for k, v := range m {
|
||||
//TODO: can this be done without sprintf?
|
||||
vals.Set(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// URLQuery gets an encoded URL query representing the given
|
||||
// Obj. This function requires that the wrapped object be a
|
||||
// map[string]interface{}
|
||||
func (m Map) URLQuery() (string, error) {
|
||||
return m.URLValues().Encode(), nil
|
||||
}
|
66
src/vendor/github.com/stretchr/objx/doc.go
generated
vendored
Normal file
66
src/vendor/github.com/stretchr/objx/doc.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
Objx - Go package for dealing with maps, slices, JSON and other data.
|
||||
|
||||
Overview
|
||||
|
||||
Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
|
||||
a powerful `Get` method (among others) that allows you to easily and quickly get
|
||||
access to data within the map, without having to worry too much about type assertions,
|
||||
missing data, default values etc.
|
||||
|
||||
Pattern
|
||||
|
||||
Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy.
|
||||
Call one of the `objx.` functions to create your `objx.Map` to get going:
|
||||
|
||||
m, err := objx.FromJSON(json)
|
||||
|
||||
NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
|
||||
the rest will be optimistic and try to figure things out without panicking.
|
||||
|
||||
Use `Get` to access the value you're interested in. You can use dot and array
|
||||
notation too:
|
||||
|
||||
m.Get("places[0].latlng")
|
||||
|
||||
Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type.
|
||||
|
||||
if m.Get("code").IsStr() { // Your code... }
|
||||
|
||||
Or you can just assume the type, and use one of the strong type methods to extract the real value:
|
||||
|
||||
m.Get("code").Int()
|
||||
|
||||
If there's no value there (or if it's the wrong type) then a default value will be returned,
|
||||
or you can be explicit about the default value.
|
||||
|
||||
Get("code").Int(-1)
|
||||
|
||||
If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating,
|
||||
manipulating and selecting that data. You can find out more by exploring the index below.
|
||||
|
||||
Reading data
|
||||
|
||||
A simple example of how to use Objx:
|
||||
|
||||
// Use MustFromJSON to make an objx.Map from some JSON
|
||||
m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
|
||||
|
||||
// Get the details
|
||||
name := m.Get("name").Str()
|
||||
age := m.Get("age").Int()
|
||||
|
||||
// Get their nickname (or use their name if they don't have one)
|
||||
nickname := m.Get("nickname").Str(name)
|
||||
|
||||
Ranging
|
||||
|
||||
Since `objx.Map` is a `map[string]interface{}` you can treat it as such.
|
||||
For example, to `range` the data, do what you would expect:
|
||||
|
||||
m := objx.MustFromJSON(json)
|
||||
for key, value := range m {
|
||||
// Your code...
|
||||
}
|
||||
*/
|
||||
package objx
|
190
src/vendor/github.com/stretchr/objx/map.go
generated
vendored
Normal file
190
src/vendor/github.com/stretchr/objx/map.go
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MSIConvertable is an interface that defines methods for converting your
|
||||
// custom types to a map[string]interface{} representation.
|
||||
type MSIConvertable interface {
|
||||
// MSI gets a map[string]interface{} (msi) representing the
|
||||
// object.
|
||||
MSI() map[string]interface{}
|
||||
}
|
||||
|
||||
// Map provides extended functionality for working with
|
||||
// untyped data, in particular map[string]interface (msi).
|
||||
type Map map[string]interface{}
|
||||
|
||||
// Value returns the internal value instance
|
||||
func (m Map) Value() *Value {
|
||||
return &Value{data: m}
|
||||
}
|
||||
|
||||
// Nil represents a nil Map.
|
||||
var Nil = New(nil)
|
||||
|
||||
// New creates a new Map containing the map[string]interface{} in the data argument.
|
||||
// If the data argument is not a map[string]interface, New attempts to call the
|
||||
// MSI() method on the MSIConvertable interface to create one.
|
||||
func New(data interface{}) Map {
|
||||
if _, ok := data.(map[string]interface{}); !ok {
|
||||
if converter, ok := data.(MSIConvertable); ok {
|
||||
data = converter.MSI()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return Map(data.(map[string]interface{}))
|
||||
}
|
||||
|
||||
// MSI creates a map[string]interface{} and puts it inside a new Map.
|
||||
//
|
||||
// The arguments follow a key, value pattern.
|
||||
//
|
||||
//
|
||||
// Returns nil if any key argument is non-string or if there are an odd number of arguments.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To easily create Maps:
|
||||
//
|
||||
// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
|
||||
//
|
||||
// // creates an Map equivalent to
|
||||
// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}}
|
||||
func MSI(keyAndValuePairs ...interface{}) Map {
|
||||
newMap := Map{}
|
||||
keyAndValuePairsLen := len(keyAndValuePairs)
|
||||
if keyAndValuePairsLen%2 != 0 {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < keyAndValuePairsLen; i = i + 2 {
|
||||
key := keyAndValuePairs[i]
|
||||
value := keyAndValuePairs[i+1]
|
||||
|
||||
// make sure the key is a string
|
||||
keyString, keyStringOK := key.(string)
|
||||
if !keyStringOK {
|
||||
return nil
|
||||
}
|
||||
newMap[keyString] = value
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// ****** Conversion Constructors
|
||||
|
||||
// MustFromJSON creates a new Map containing the data specified in the
|
||||
// jsonString.
|
||||
//
|
||||
// Panics if the JSON is invalid.
|
||||
func MustFromJSON(jsonString string) Map {
|
||||
o, err := FromJSON(jsonString)
|
||||
if err != nil {
|
||||
panic("objx: MustFromJSON failed with error: " + err.Error())
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// FromJSON creates a new Map containing the data specified in the
|
||||
// jsonString.
|
||||
//
|
||||
// Returns an error if the JSON is invalid.
|
||||
func FromJSON(jsonString string) (Map, error) {
|
||||
var data interface{}
|
||||
err := json.Unmarshal([]byte(jsonString), &data)
|
||||
if err != nil {
|
||||
return Nil, err
|
||||
}
|
||||
return New(data), nil
|
||||
}
|
||||
|
||||
// FromBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func FromBase64(base64String string) (Map, error) {
|
||||
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
|
||||
decoded, err := ioutil.ReadAll(decoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FromJSON(string(decoded))
|
||||
}
|
||||
|
||||
// MustFromBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string and panics if there is an error.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func MustFromBase64(base64String string) Map {
|
||||
result, err := FromBase64(base64String)
|
||||
if err != nil {
|
||||
panic("objx: MustFromBase64 failed with error: " + err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FromSignedBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string.
|
||||
//
|
||||
// The string is an encoded JSON string returned by SignedBase64
|
||||
func FromSignedBase64(base64String, key string) (Map, error) {
|
||||
parts := strings.Split(base64String, SignatureSeparator)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.New("objx: Signed base64 string is malformed")
|
||||
}
|
||||
|
||||
sig := HashWithKey(parts[0], key)
|
||||
if parts[1] != sig {
|
||||
return nil, errors.New("objx: Signature for base64 data does not match")
|
||||
}
|
||||
return FromBase64(parts[0])
|
||||
}
|
||||
|
||||
// MustFromSignedBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string and panics if there is an error.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func MustFromSignedBase64(base64String, key string) Map {
|
||||
result, err := FromSignedBase64(base64String, key)
|
||||
if err != nil {
|
||||
panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FromURLQuery generates a new Obj by parsing the specified
|
||||
// query.
|
||||
//
|
||||
// For queries with multiple values, the first value is selected.
|
||||
func FromURLQuery(query string) (Map, error) {
|
||||
vals, err := url.ParseQuery(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := Map{}
|
||||
for k, vals := range vals {
|
||||
m[k] = vals[0]
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// MustFromURLQuery generates a new Obj by parsing the specified
|
||||
// query.
|
||||
//
|
||||
// For queries with multiple values, the first value is selected.
|
||||
//
|
||||
// Panics if it encounters an error
|
||||
func MustFromURLQuery(query string) Map {
|
||||
o, err := FromURLQuery(query)
|
||||
if err != nil {
|
||||
panic("objx: MustFromURLQuery failed with error: " + err.Error())
|
||||
}
|
||||
return o
|
||||
}
|
77
src/vendor/github.com/stretchr/objx/mutations.go
generated
vendored
Normal file
77
src/vendor/github.com/stretchr/objx/mutations.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package objx
|
||||
|
||||
// Exclude returns a new Map with the keys in the specified []string
|
||||
// excluded.
|
||||
func (m Map) Exclude(exclude []string) Map {
|
||||
excluded := make(Map)
|
||||
for k, v := range m {
|
||||
if !contains(exclude, k) {
|
||||
excluded[k] = v
|
||||
}
|
||||
}
|
||||
return excluded
|
||||
}
|
||||
|
||||
// Copy creates a shallow copy of the Obj.
|
||||
func (m Map) Copy() Map {
|
||||
copied := Map{}
|
||||
for k, v := range m {
|
||||
copied[k] = v
|
||||
}
|
||||
return copied
|
||||
}
|
||||
|
||||
// Merge blends the specified map with a copy of this map and returns the result.
|
||||
//
|
||||
// Keys that appear in both will be selected from the specified map.
|
||||
// This method requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) Merge(merge Map) Map {
|
||||
return m.Copy().MergeHere(merge)
|
||||
}
|
||||
|
||||
// MergeHere blends the specified map with this map and returns the current map.
|
||||
//
|
||||
// Keys that appear in both will be selected from the specified map. The original map
|
||||
// will be modified. This method requires that
|
||||
// the wrapped object be a map[string]interface{}
|
||||
func (m Map) MergeHere(merge Map) Map {
|
||||
for k, v := range merge {
|
||||
m[k] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Transform builds a new Obj giving the transformer a chance
|
||||
// to change the keys and values as it goes. This method requires that
|
||||
// the wrapped object be a map[string]interface{}
|
||||
func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map {
|
||||
newMap := Map{}
|
||||
for k, v := range m {
|
||||
modifiedKey, modifiedVal := transformer(k, v)
|
||||
newMap[modifiedKey] = modifiedVal
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// TransformKeys builds a new map using the specified key mapping.
|
||||
//
|
||||
// Unspecified keys will be unaltered.
|
||||
// This method requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) TransformKeys(mapping map[string]string) Map {
|
||||
return m.Transform(func(key string, value interface{}) (string, interface{}) {
|
||||
if newKey, ok := mapping[key]; ok {
|
||||
return newKey, value
|
||||
}
|
||||
return key, value
|
||||
})
|
||||
}
|
||||
|
||||
// Checks if a string slice contains a string
|
||||
func contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
12
src/vendor/github.com/stretchr/objx/security.go
generated
vendored
Normal file
12
src/vendor/github.com/stretchr/objx/security.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// HashWithKey hashes the specified string using the security key
|
||||
func HashWithKey(data, key string) string {
|
||||
d := sha1.Sum([]byte(data + ":" + key))
|
||||
return hex.EncodeToString(d[:])
|
||||
}
|
17
src/vendor/github.com/stretchr/objx/tests.go
generated
vendored
Normal file
17
src/vendor/github.com/stretchr/objx/tests.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
package objx
|
||||
|
||||
// Has gets whether there is something at the specified selector
|
||||
// or not.
|
||||
//
|
||||
// If m is nil, Has will always return false.
|
||||
func (m Map) Has(selector string) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
return !m.Get(selector).IsNil()
|
||||
}
|
||||
|
||||
// IsNil gets whether the data is nil or not.
|
||||
func (v *Value) IsNil() bool {
|
||||
return v == nil || v.data == nil
|
||||
}
|
2501
src/vendor/github.com/stretchr/objx/type_specific_codegen.go
generated
vendored
Normal file
2501
src/vendor/github.com/stretchr/objx/type_specific_codegen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
53
src/vendor/github.com/stretchr/objx/value.go
generated
vendored
Normal file
53
src/vendor/github.com/stretchr/objx/value.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Value provides methods for extracting interface{} data in various
|
||||
// types.
|
||||
type Value struct {
|
||||
// data contains the raw data being managed by this Value
|
||||
data interface{}
|
||||
}
|
||||
|
||||
// Data returns the raw data contained by this Value
|
||||
func (v *Value) Data() interface{} {
|
||||
return v.data
|
||||
}
|
||||
|
||||
// String returns the value always as a string
|
||||
func (v *Value) String() string {
|
||||
switch {
|
||||
case v.IsStr():
|
||||
return v.Str()
|
||||
case v.IsBool():
|
||||
return strconv.FormatBool(v.Bool())
|
||||
case v.IsFloat32():
|
||||
return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32)
|
||||
case v.IsFloat64():
|
||||
return strconv.FormatFloat(v.Float64(), 'f', -1, 64)
|
||||
case v.IsInt():
|
||||
return strconv.FormatInt(int64(v.Int()), 10)
|
||||
case v.IsInt8():
|
||||
return strconv.FormatInt(int64(v.Int8()), 10)
|
||||
case v.IsInt16():
|
||||
return strconv.FormatInt(int64(v.Int16()), 10)
|
||||
case v.IsInt32():
|
||||
return strconv.FormatInt(int64(v.Int32()), 10)
|
||||
case v.IsInt64():
|
||||
return strconv.FormatInt(v.Int64(), 10)
|
||||
case v.IsUint():
|
||||
return strconv.FormatUint(uint64(v.Uint()), 10)
|
||||
case v.IsUint8():
|
||||
return strconv.FormatUint(uint64(v.Uint8()), 10)
|
||||
case v.IsUint16():
|
||||
return strconv.FormatUint(uint64(v.Uint16()), 10)
|
||||
case v.IsUint32():
|
||||
return strconv.FormatUint(uint64(v.Uint32()), 10)
|
||||
case v.IsUint64():
|
||||
return strconv.FormatUint(v.Uint64(), 10)
|
||||
}
|
||||
return fmt.Sprintf("%#v", v.Data())
|
||||
}
|
35
src/vendor/github.com/stretchr/testify/LICENSE
generated
vendored
35
src/vendor/github.com/stretchr/testify/LICENSE
generated
vendored
@ -1,22 +1,21 @@
|
||||
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
|
||||
MIT License
|
||||
|
||||
Please consider promoting this project if you find it useful.
|
||||
Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
201
src/vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
201
src/vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
@ -13,6 +13,9 @@ import (
|
||||
|
||||
// Conditionf uses a Comparison to assert a complex condition.
|
||||
func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Condition(t, comp, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -22,14 +25,18 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Contains(t, s, contains, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return DirExists(t, path, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -37,10 +44,11 @@ func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
|
||||
// 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"))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -48,9 +56,10 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Empty(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -58,12 +67,13 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo
|
||||
//
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Equal(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -72,9 +82,10 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -82,9 +93,10 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -94,62 +106,80 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
|
||||
// if assert.Errorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Error(t, err, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Failf reports a failure through
|
||||
func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Fail(t, failureMessage, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// FailNowf fails test
|
||||
func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Falsef asserts that the specified value is false.
|
||||
//
|
||||
// assert.Falsef(t, myBool, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return False(t, value, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return FileExists(t, path, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPBodyContainsf asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPBodyNotContainsf asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -159,6 +189,9 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -168,6 +201,9 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string,
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -177,6 +213,9 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -184,51 +223,69 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin
|
||||
//
|
||||
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
|
||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// 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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// InEpsilonf asserts that expected and actual have a relative error less than epsilon
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// IsTypef asserts that the specified objects are of the same type.
|
||||
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// JSONEqf asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -236,18 +293,20 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Len(t, object, length, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Nilf asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Nil(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -257,9 +316,10 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool
|
||||
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NoError(t, err, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -269,9 +329,10 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -281,9 +342,10 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEmpty(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -291,29 +353,32 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{})
|
||||
//
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotNil(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotPanics(t, f, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -321,9 +386,10 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo
|
||||
//
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -331,23 +397,28 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ..
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotZerof asserts that i is not the zero value for its type and returns the truth.
|
||||
// NotZerof asserts that i is not the zero value for its type.
|
||||
func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotZero(t, i, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Panics(t, f, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -355,9 +426,10 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -365,9 +437,10 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
|
||||
//
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
@ -375,31 +448,37 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Subset(t, list, subset, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Truef asserts that the specified value is true.
|
||||
//
|
||||
// assert.Truef(t, myBool, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return True(t, value, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Zerof asserts that i is the zero value for its type and returns the truth.
|
||||
// Zerof asserts that i is the zero value for its type.
|
||||
func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Zero(t, i, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
1
src/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl
generated
vendored
1
src/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl
generated
vendored
@ -1,4 +1,5 @@
|
||||
{{.CommentFormat}}
|
||||
func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool {
|
||||
if h, ok := t.(tHelper); ok { h.Helper() }
|
||||
return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}})
|
||||
}
|
||||
|
402
src/vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
402
src/vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
@ -13,11 +13,17 @@ import (
|
||||
|
||||
// Condition uses a Comparison to assert a complex condition.
|
||||
func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Condition(a.t, comp, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Conditionf uses a Comparison to assert a complex condition.
|
||||
func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Conditionf(a.t, comp, msg, args...)
|
||||
}
|
||||
|
||||
@ -27,9 +33,10 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}
|
||||
// a.Contains("Hello World", "World")
|
||||
// a.Contains(["Hello", "World"], "World")
|
||||
// a.Contains({"Hello": "World"}, "Hello")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Contains(a.t, s, contains, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -39,19 +46,26 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ..
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return DirExistsf(a.t, path, msg, args...)
|
||||
}
|
||||
|
||||
@ -59,10 +73,11 @@ func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bo
|
||||
// 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]))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2])
|
||||
func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ElementsMatch(a.t, listA, listB, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -70,10 +85,11 @@ func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndA
|
||||
// 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"))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ElementsMatchf(a.t, listA, listB, msg, args...)
|
||||
}
|
||||
|
||||
@ -81,9 +97,10 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// a.Empty(obj)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Empty(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -91,9 +108,10 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// a.Emptyf(obj, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Emptyf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
@ -101,12 +119,13 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{})
|
||||
//
|
||||
// a.Equal(123, 123)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Equal(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -115,9 +134,10 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// a.EqualError(err, expectedErrorString)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualError(a.t, theError, errString, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -126,9 +146,10 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualErrorf(a.t, theError, errString, msg, args...)
|
||||
}
|
||||
|
||||
@ -136,9 +157,10 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a
|
||||
// and equal.
|
||||
//
|
||||
// a.EqualValues(uint32(123), int32(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualValues(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -146,9 +168,10 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
|
||||
// and equal.
|
||||
//
|
||||
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualValuesf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
@ -156,12 +179,13 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg
|
||||
//
|
||||
// a.Equalf(123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Equalf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
@ -171,9 +195,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string
|
||||
// if a.Error(err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Error(a.t, err, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -183,115 +208,150 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
|
||||
// if a.Errorf(err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Errorf(a.t, err, msg, args...)
|
||||
}
|
||||
|
||||
// Exactly asserts that two objects are equal in value and type.
|
||||
//
|
||||
// a.Exactly(int32(123), int64(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Exactlyf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// Fail reports a failure through
|
||||
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Fail(a.t, failureMessage, msgAndArgs...)
|
||||
}
|
||||
|
||||
// FailNow fails test
|
||||
func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return FailNow(a.t, failureMessage, msgAndArgs...)
|
||||
}
|
||||
|
||||
// FailNowf fails test
|
||||
func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return FailNowf(a.t, failureMessage, msg, args...)
|
||||
}
|
||||
|
||||
// Failf reports a failure through
|
||||
func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Failf(a.t, failureMessage, msg, args...)
|
||||
}
|
||||
|
||||
// False asserts that the specified value is false.
|
||||
//
|
||||
// a.False(myBool)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return False(a.t, value, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Falsef asserts that the specified value is false.
|
||||
//
|
||||
// a.Falsef(myBool, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return FileExistsf(a.t, path, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPBodyContains asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...)
|
||||
}
|
||||
|
||||
@ -301,6 +361,9 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPError(a.t, handler, method, url, values, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -310,6 +373,9 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPErrorf(a.t, handler, method, url, values, msg, args...)
|
||||
}
|
||||
|
||||
@ -319,6 +385,9 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -328,6 +397,9 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
|
||||
}
|
||||
|
||||
@ -337,6 +409,9 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -346,6 +421,9 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPSuccessf(a.t, handler, method, url, values, msg, args...)
|
||||
}
|
||||
|
||||
@ -353,6 +431,9 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s
|
||||
//
|
||||
// a.Implements((*MyInterface)(nil), new(MyObject))
|
||||
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Implements(a.t, interfaceObject, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -360,96 +441,129 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{},
|
||||
//
|
||||
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
|
||||
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return InDeltaf(a.t, expected, actual, delta, msg, args...)
|
||||
}
|
||||
|
||||
// InEpsilon asserts that expected and actual have a relative error less than epsilon
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...)
|
||||
}
|
||||
|
||||
// InEpsilonf asserts that expected and actual have a relative error less than epsilon
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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"}`)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return JSONEqf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
@ -457,9 +571,10 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ..
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// a.Len(mySlice, 3)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Len(a.t, object, length, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -467,27 +582,30 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// a.Lenf(mySlice, 3, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Lenf(a.t, object, length, msg, args...)
|
||||
}
|
||||
|
||||
// Nil asserts that the specified object is nil.
|
||||
//
|
||||
// a.Nil(err)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Nil(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Nilf asserts that the specified object is nil.
|
||||
//
|
||||
// a.Nilf(err, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Nilf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
@ -497,9 +615,10 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b
|
||||
// if a.NoError(err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NoError(a.t, err, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -509,9 +628,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
|
||||
// if a.NoErrorf(err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NoErrorf(a.t, err, msg, args...)
|
||||
}
|
||||
|
||||
@ -521,9 +641,10 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
|
||||
// a.NotContains("Hello World", "Earth")
|
||||
// a.NotContains(["Hello", "World"], "Earth")
|
||||
// a.NotContains({"Hello": "World"}, "Earth")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotContains(a.t, s, contains, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -533,9 +654,10 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotContainsf(a.t, s, contains, msg, args...)
|
||||
}
|
||||
|
||||
@ -545,9 +667,10 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin
|
||||
// if a.NotEmpty(obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEmpty(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -557,9 +680,10 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo
|
||||
// if a.NotEmptyf(obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEmptyf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
@ -567,11 +691,12 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface
|
||||
//
|
||||
// a.NotEqual(obj1, obj2)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEqual(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -579,47 +704,52 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr
|
||||
//
|
||||
// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEqualf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// a.NotNil(err)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotNil(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// a.NotNilf(err, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotNilf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// a.NotPanics(func(){ RemainCalm() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotPanicsf(a.t, f, msg, args...)
|
||||
}
|
||||
|
||||
@ -627,9 +757,10 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}
|
||||
//
|
||||
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
|
||||
// a.NotRegexp("^start", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotRegexp(a.t, rx, str, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -637,9 +768,10 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in
|
||||
//
|
||||
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
|
||||
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotRegexpf(a.t, rx, str, msg, args...)
|
||||
}
|
||||
|
||||
@ -647,9 +779,10 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotSubset(a.t, list, subset, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -657,28 +790,36 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotSubsetf(a.t, list, subset, msg, args...)
|
||||
}
|
||||
|
||||
// NotZero asserts that i is not the zero value for its type and returns the truth.
|
||||
// NotZero asserts that i is not the zero value for its type.
|
||||
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotZero(a.t, i, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotZerof asserts that i is not the zero value for its type and returns the truth.
|
||||
// NotZerof asserts that i is not the zero value for its type.
|
||||
func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotZerof(a.t, i, msg, args...)
|
||||
}
|
||||
|
||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// a.Panics(func(){ GoCrazy() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Panics(a.t, f, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -686,9 +827,10 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// a.PanicsWithValue("crazy error", func(){ GoCrazy() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return PanicsWithValue(a.t, expected, f, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -696,18 +838,20 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Panicsf(a.t, f, msg, args...)
|
||||
}
|
||||
|
||||
@ -715,9 +859,10 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b
|
||||
//
|
||||
// a.Regexp(regexp.MustCompile("start"), "it's starting")
|
||||
// a.Regexp("start...$", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Regexp(a.t, rx, str, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -725,9 +870,10 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
|
||||
//
|
||||
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
|
||||
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Regexpf(a.t, rx, str, msg, args...)
|
||||
}
|
||||
|
||||
@ -735,9 +881,10 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args .
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Subset(a.t, list, subset, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -745,54 +892,65 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Subsetf(a.t, list, subset, msg, args...)
|
||||
}
|
||||
|
||||
// True asserts that the specified value is true.
|
||||
//
|
||||
// a.True(myBool)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return True(a.t, value, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Truef asserts that the specified value is true.
|
||||
//
|
||||
// a.Truef(myBool, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return WithinDurationf(a.t, expected, actual, delta, msg, args...)
|
||||
}
|
||||
|
||||
// Zero asserts that i is the zero value for its type and returns the truth.
|
||||
// Zero asserts that i is the zero value for its type.
|
||||
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Zero(a.t, i, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Zerof asserts that i is the zero value for its type and returns the truth.
|
||||
// Zerof asserts that i is the zero value for its type.
|
||||
func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Zerof(a.t, i, msg, args...)
|
||||
}
|
||||
|
1
src/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl
generated
vendored
1
src/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl
generated
vendored
@ -1,4 +1,5 @@
|
||||
{{.CommentWithoutT "a"}}
|
||||
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool {
|
||||
if h, ok := a.t.(tHelper); ok { h.Helper() }
|
||||
return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
|
||||
}
|
||||
|
308
src/vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
308
src/vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
@ -27,6 +27,22 @@ type TestingT interface {
|
||||
Errorf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
|
||||
// for table driven tests.
|
||||
type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool
|
||||
|
||||
// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
|
||||
// for table driven tests.
|
||||
type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool
|
||||
|
||||
// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
|
||||
// for table driven tests.
|
||||
type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
|
||||
|
||||
// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful
|
||||
// for table driven tests.
|
||||
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
|
||||
|
||||
// Comparison a custom function that returns true on success and false on failure
|
||||
type Comparison func() (success bool)
|
||||
|
||||
@ -38,21 +54,23 @@ type Comparison func() (success bool)
|
||||
//
|
||||
// This function does no assertion of any kind.
|
||||
func ObjectsAreEqual(expected, actual interface{}) bool {
|
||||
|
||||
if expected == nil || actual == nil {
|
||||
return expected == actual
|
||||
}
|
||||
if exp, ok := expected.([]byte); ok {
|
||||
act, ok := actual.([]byte)
|
||||
if !ok {
|
||||
return false
|
||||
} else if exp == nil || act == nil {
|
||||
return exp == nil && act == nil
|
||||
}
|
||||
return bytes.Equal(exp, act)
|
||||
}
|
||||
return reflect.DeepEqual(expected, actual)
|
||||
|
||||
exp, ok := expected.([]byte)
|
||||
if !ok {
|
||||
return reflect.DeepEqual(expected, actual)
|
||||
}
|
||||
|
||||
act, ok := actual.([]byte)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if exp == nil || act == nil {
|
||||
return exp == nil && act == nil
|
||||
}
|
||||
return bytes.Equal(exp, act)
|
||||
}
|
||||
|
||||
// ObjectsAreEqualValues gets whether two objects are equal, or if their
|
||||
@ -156,27 +174,16 @@ func isTest(name, prefix string) bool {
|
||||
return !unicode.IsLower(rune)
|
||||
}
|
||||
|
||||
// getWhitespaceString returns a string that is long enough to overwrite the default
|
||||
// output from the go testing framework.
|
||||
func getWhitespaceString() string {
|
||||
|
||||
_, file, line, ok := runtime.Caller(1)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
parts := strings.Split(file, "/")
|
||||
file = parts[len(parts)-1]
|
||||
|
||||
return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line)))
|
||||
|
||||
}
|
||||
|
||||
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
||||
if len(msgAndArgs) == 0 || msgAndArgs == nil {
|
||||
return ""
|
||||
}
|
||||
if len(msgAndArgs) == 1 {
|
||||
return msgAndArgs[0].(string)
|
||||
msg := msgAndArgs[0]
|
||||
if msgAsStr, ok := msg.(string); ok {
|
||||
return msgAsStr
|
||||
}
|
||||
return fmt.Sprintf("%+v", msg)
|
||||
}
|
||||
if len(msgAndArgs) > 1 {
|
||||
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
|
||||
@ -195,7 +202,7 @@ func indentMessageLines(message string, longestLabelLen int) string {
|
||||
// no need to align first line because it starts at the correct location (after the label)
|
||||
if i != 0 {
|
||||
// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
|
||||
outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
|
||||
outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
|
||||
}
|
||||
outBuf.WriteString(scanner.Text())
|
||||
}
|
||||
@ -209,6 +216,9 @@ type failNower interface {
|
||||
|
||||
// FailNow fails test
|
||||
func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
Fail(t, failureMessage, msgAndArgs...)
|
||||
|
||||
// We cannot extend TestingT with FailNow() and
|
||||
@ -227,8 +237,11 @@ func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool
|
||||
|
||||
// Fail reports a failure through
|
||||
func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
content := []labeledContent{
|
||||
{"Error Trace", strings.Join(CallerInfo(), "\n\r\t\t\t")},
|
||||
{"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")},
|
||||
{"Error", failureMessage},
|
||||
}
|
||||
|
||||
@ -244,7 +257,7 @@ func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
content = append(content, labeledContent{"Messages", message})
|
||||
}
|
||||
|
||||
t.Errorf("%s", "\r"+getWhitespaceString()+labeledOutput(content...))
|
||||
t.Errorf("\n%s", ""+labeledOutput(content...))
|
||||
|
||||
return false
|
||||
}
|
||||
@ -256,7 +269,7 @@ type labeledContent struct {
|
||||
|
||||
// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
|
||||
//
|
||||
// \r\t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
// \t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
//
|
||||
// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
|
||||
// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
|
||||
@ -272,7 +285,7 @@ func labeledOutput(content ...labeledContent) string {
|
||||
}
|
||||
var output string
|
||||
for _, v := range content {
|
||||
output += "\r\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n"
|
||||
output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n"
|
||||
}
|
||||
return output
|
||||
}
|
||||
@ -281,6 +294,9 @@ func labeledOutput(content ...labeledContent) string {
|
||||
//
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
||||
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
interfaceType := reflect.TypeOf(interfaceObject).Elem()
|
||||
|
||||
if object == nil {
|
||||
@ -295,6 +311,9 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg
|
||||
|
||||
// IsType asserts that the specified objects are of the same type.
|
||||
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {
|
||||
return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...)
|
||||
@ -307,12 +326,13 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs
|
||||
//
|
||||
// assert.Equal(t, 123, 123)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if err := validateEqualArgs(expected, actual); err != nil {
|
||||
return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)",
|
||||
expected, actual, err), msgAndArgs...)
|
||||
@ -350,9 +370,10 @@ func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValues(t, uint32(123), int32(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if !ObjectsAreEqualValues(expected, actual) {
|
||||
diff := diff(expected, actual)
|
||||
@ -369,15 +390,16 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
|
||||
// Exactly asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
aType := reflect.TypeOf(expected)
|
||||
bType := reflect.TypeOf(actual)
|
||||
|
||||
if aType != bType {
|
||||
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\r\t%v != %v", aType, bType), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
|
||||
}
|
||||
|
||||
return Equal(t, expected, actual, msgAndArgs...)
|
||||
@ -387,15 +409,27 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNil(t, err)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if !isNil(object) {
|
||||
return true
|
||||
}
|
||||
return Fail(t, "Expected value not to be nil.", msgAndArgs...)
|
||||
}
|
||||
|
||||
// containsKind checks if a specified kind in the slice of kinds.
|
||||
func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
|
||||
for i := 0; i < len(kinds); i++ {
|
||||
if kind == kinds[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isNil checks if a specified object is nil or not, without Failing.
|
||||
func isNil(object interface{}) bool {
|
||||
if object == nil {
|
||||
@ -404,7 +438,14 @@ func isNil(object interface{}) bool {
|
||||
|
||||
value := reflect.ValueOf(object)
|
||||
kind := value.Kind()
|
||||
if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
|
||||
isNilableKind := containsKind(
|
||||
[]reflect.Kind{
|
||||
reflect.Chan, reflect.Func,
|
||||
reflect.Interface, reflect.Map,
|
||||
reflect.Ptr, reflect.Slice},
|
||||
kind)
|
||||
|
||||
if isNilableKind && value.IsNil() {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -414,9 +455,10 @@ func isNil(object interface{}) bool {
|
||||
// Nil asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nil(t, err)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if isNil(object) {
|
||||
return true
|
||||
}
|
||||
@ -455,9 +497,10 @@ func isEmpty(object interface{}) bool {
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Empty(t, obj)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
pass := isEmpty(object)
|
||||
if !pass {
|
||||
@ -474,9 +517,10 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
pass := !isEmpty(object)
|
||||
if !pass {
|
||||
@ -503,9 +547,10 @@ func getLen(x interface{}) (ok bool, length int) {
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Len(t, mySlice, 3)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
ok, l := getLen(object)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...)
|
||||
@ -520,9 +565,15 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
|
||||
// True asserts that the specified value is true.
|
||||
//
|
||||
// assert.True(t, myBool)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if h, ok := t.(interface {
|
||||
Helper()
|
||||
}); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if value != true {
|
||||
return Fail(t, "Should be true", msgAndArgs...)
|
||||
@ -535,9 +586,10 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
// False asserts that the specified value is false.
|
||||
//
|
||||
// assert.False(t, myBool)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if value != false {
|
||||
return Fail(t, "Should be false", msgAndArgs...)
|
||||
@ -551,11 +603,12 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
//
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if err := validateEqualArgs(expected, actual); err != nil {
|
||||
return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)",
|
||||
expected, actual, err), msgAndArgs...)
|
||||
@ -613,9 +666,10 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
||||
// assert.Contains(t, "Hello World", "World")
|
||||
// assert.Contains(t, ["Hello", "World"], "World")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
ok, found := includeElement(s, contains)
|
||||
if !ok {
|
||||
@ -635,9 +689,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
|
||||
// assert.NotContains(t, "Hello World", "Earth")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
ok, found := includeElement(s, contains)
|
||||
if !ok {
|
||||
@ -655,9 +710,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if subset == nil {
|
||||
return true // we consider nil to be equal to the nil set
|
||||
}
|
||||
@ -698,9 +754,10 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
||||
// 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]")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if subset == nil {
|
||||
return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
|
||||
}
|
||||
@ -741,10 +798,11 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
||||
// 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]))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
|
||||
func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if isEmpty(listA) && isEmpty(listB) {
|
||||
return true
|
||||
}
|
||||
@ -795,6 +853,9 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface
|
||||
|
||||
// Condition uses a Comparison to assert a complex condition.
|
||||
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
result := comp()
|
||||
if !result {
|
||||
Fail(t, "Condition failed!", msgAndArgs...)
|
||||
@ -831,12 +892,13 @@ func didPanic(f PanicTestFunc) (bool, interface{}) {
|
||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panics(t, func(){ GoCrazy() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
|
||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
@ -846,16 +908,17 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
funcDidPanic, panicValue := didPanic(f)
|
||||
if !funcDidPanic {
|
||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
|
||||
}
|
||||
if panicValue != expected {
|
||||
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%v\n\r\tPanic value:\t%v", f, expected, panicValue), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
@ -864,12 +927,13 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr
|
||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanics(t, func(){ RemainCalm() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
|
||||
return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
@ -878,9 +942,10 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
// WithinDuration asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
dt := expected.Sub(actual)
|
||||
if dt < -delta || dt > delta {
|
||||
@ -929,9 +994,10 @@ func toFloat(x interface{}) (float64, bool) {
|
||||
// InDelta asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
af, aok := toFloat(expected)
|
||||
bf, bok := toFloat(actual)
|
||||
@ -958,6 +1024,9 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs
|
||||
|
||||
// InDeltaSlice is the same as InDelta, except it compares two slices.
|
||||
func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if expected == nil || actual == nil ||
|
||||
reflect.TypeOf(actual).Kind() != reflect.Slice ||
|
||||
reflect.TypeOf(expected).Kind() != reflect.Slice {
|
||||
@ -979,6 +1048,9 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
|
||||
|
||||
// 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, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if expected == nil || actual == nil ||
|
||||
reflect.TypeOf(actual).Kind() != reflect.Map ||
|
||||
reflect.TypeOf(expected).Kind() != reflect.Map {
|
||||
@ -989,7 +1061,7 @@ func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, m
|
||||
actualMap := reflect.ValueOf(actual)
|
||||
|
||||
if expectedMap.Len() != actualMap.Len() {
|
||||
return Fail(t, "Arguments must have the same numbe of keys", msgAndArgs...)
|
||||
return Fail(t, "Arguments must have the same number of keys", msgAndArgs...)
|
||||
}
|
||||
|
||||
for _, k := range expectedMap.MapKeys() {
|
||||
@ -1035,9 +1107,10 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
|
||||
}
|
||||
|
||||
// InEpsilon asserts that expected and actual have a relative error less than epsilon
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
actualEpsilon, err := calcRelativeError(expected, actual)
|
||||
if err != nil {
|
||||
return Fail(t, err.Error(), msgAndArgs...)
|
||||
@ -1052,6 +1125,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd
|
||||
|
||||
// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
|
||||
func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if expected == nil || actual == nil ||
|
||||
reflect.TypeOf(actual).Kind() != reflect.Slice ||
|
||||
reflect.TypeOf(expected).Kind() != reflect.Slice {
|
||||
@ -1081,9 +1157,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
|
||||
// if assert.NoError(t, err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if err != nil {
|
||||
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
|
||||
}
|
||||
@ -1097,9 +1174,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
// if assert.Error(t, err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return Fail(t, "An error is expected but got nil.", msgAndArgs...)
|
||||
@ -1113,9 +1191,10 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualError(t, err, expectedErrorString)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if !Error(t, theError, msgAndArgs...) {
|
||||
return false
|
||||
}
|
||||
@ -1148,9 +1227,10 @@ func matchRegexp(rx interface{}, str interface{}) bool {
|
||||
//
|
||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
||||
// assert.Regexp(t, "start...$", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
match := matchRegexp(rx, str)
|
||||
|
||||
@ -1165,9 +1245,10 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface
|
||||
//
|
||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
match := matchRegexp(rx, str)
|
||||
|
||||
if match {
|
||||
@ -1178,16 +1259,22 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
|
||||
|
||||
}
|
||||
|
||||
// Zero asserts that i is the zero value for its type and returns the truth.
|
||||
// Zero asserts that i is the zero value for its type.
|
||||
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
|
||||
return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NotZero asserts that i is not the zero value for its type and returns the truth.
|
||||
// NotZero asserts that i is not the zero value for its type.
|
||||
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
|
||||
return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
|
||||
}
|
||||
@ -1196,6 +1283,9 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@ -1211,6 +1301,9 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// 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{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@ -1227,9 +1320,10 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
||||
// JSONEq asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
var expectedJSONAsInterface, actualJSONAsInterface interface{}
|
||||
|
||||
if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil {
|
||||
@ -1255,7 +1349,7 @@ func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
|
||||
}
|
||||
|
||||
// diff returns a diff of both values as long as both are of the same type and
|
||||
// are a struct, map, slice or array. Otherwise it returns an empty string.
|
||||
// are a struct, map, slice, array or string. Otherwise it returns an empty string.
|
||||
func diff(expected interface{}, actual interface{}) string {
|
||||
if expected == nil || actual == nil {
|
||||
return ""
|
||||
@ -1268,12 +1362,18 @@ func diff(expected interface{}, actual interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array {
|
||||
if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String {
|
||||
return ""
|
||||
}
|
||||
|
||||
e := spewConfig.Sdump(expected)
|
||||
a := spewConfig.Sdump(actual)
|
||||
var e, a string
|
||||
if et != reflect.TypeOf("") {
|
||||
e = spewConfig.Sdump(expected)
|
||||
a = spewConfig.Sdump(actual)
|
||||
} else {
|
||||
e = expected.(string)
|
||||
a = actual.(string)
|
||||
}
|
||||
|
||||
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(e),
|
||||
@ -1310,3 +1410,7 @@ var spewConfig = spew.ConfigState{
|
||||
DisableCapacities: true,
|
||||
SortKeys: true,
|
||||
}
|
||||
|
||||
type tHelper interface {
|
||||
Helper()
|
||||
}
|
||||
|
22
src/vendor/github.com/stretchr/testify/assert/http_assertions.go
generated
vendored
22
src/vendor/github.com/stretchr/testify/assert/http_assertions.go
generated
vendored
@ -12,10 +12,11 @@ import (
|
||||
// an error if building a new request fails.
|
||||
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) {
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
req.URL.RawQuery = values.Encode()
|
||||
handler(w, req)
|
||||
return w.Code, nil
|
||||
}
|
||||
@ -26,6 +27,9 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
@ -46,6 +50,9 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
@ -66,6 +73,9 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
@ -95,10 +105,13 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s
|
||||
// HTTPBodyContains asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// 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, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
body := HTTPBody(handler, method, url, values)
|
||||
|
||||
contains := strings.Contains(body, fmt.Sprint(str))
|
||||
@ -112,10 +125,13 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
|
||||
// HTTPBodyNotContains asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// 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, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
body := HTTPBody(handler, method, url, values)
|
||||
|
||||
contains := strings.Contains(body, fmt.Sprint(str))
|
||||
|
44
src/vendor/github.com/stretchr/testify/mock/doc.go
generated
vendored
Normal file
44
src/vendor/github.com/stretchr/testify/mock/doc.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
// Package mock provides a system by which it is possible to mock your objects
|
||||
// and verify calls are happening as expected.
|
||||
//
|
||||
// Example Usage
|
||||
//
|
||||
// The mock package provides an object, Mock, that tracks activity on another object. It is usually
|
||||
// embedded into a test object as shown below:
|
||||
//
|
||||
// type MyTestObject struct {
|
||||
// // add a Mock object instance
|
||||
// mock.Mock
|
||||
//
|
||||
// // other fields go here as normal
|
||||
// }
|
||||
//
|
||||
// When implementing the methods of an interface, you wire your functions up
|
||||
// to call the Mock.Called(args...) method, and return the appropriate values.
|
||||
//
|
||||
// For example, to mock a method that saves the name and age of a person and returns
|
||||
// the year of their birth or an error, you might write this:
|
||||
//
|
||||
// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) {
|
||||
// args := o.Called(firstname, lastname, age)
|
||||
// return args.Int(0), args.Error(1)
|
||||
// }
|
||||
//
|
||||
// The Int, Error and Bool methods are examples of strongly typed getters that take the argument
|
||||
// index position. Given this argument list:
|
||||
//
|
||||
// (12, true, "Something")
|
||||
//
|
||||
// You could read them out strongly typed like this:
|
||||
//
|
||||
// args.Int(0)
|
||||
// args.Bool(1)
|
||||
// args.String(2)
|
||||
//
|
||||
// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion:
|
||||
//
|
||||
// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine)
|
||||
//
|
||||
// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those
|
||||
// cases you should check for nil first.
|
||||
package mock
|
886
src/vendor/github.com/stretchr/testify/mock/mock.go
generated
vendored
Normal file
886
src/vendor/github.com/stretchr/testify/mock/mock.go
generated
vendored
Normal file
@ -0,0 +1,886 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
"github.com/stretchr/objx"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestingT is an interface wrapper around *testing.T
|
||||
type TestingT interface {
|
||||
Logf(format string, args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
FailNow()
|
||||
}
|
||||
|
||||
/*
|
||||
Call
|
||||
*/
|
||||
|
||||
// Call represents a method call and is used for setting expectations,
|
||||
// as well as recording activity.
|
||||
type Call struct {
|
||||
Parent *Mock
|
||||
|
||||
// The name of the method that was or will be called.
|
||||
Method string
|
||||
|
||||
// Holds the arguments of the method.
|
||||
Arguments Arguments
|
||||
|
||||
// Holds the arguments that should be returned when
|
||||
// this method is called.
|
||||
ReturnArguments Arguments
|
||||
|
||||
// Holds the caller info for the On() call
|
||||
callerInfo []string
|
||||
|
||||
// The number of times to return the return arguments when setting
|
||||
// expectations. 0 means to always return the value.
|
||||
Repeatability int
|
||||
|
||||
// Amount of times this call has been called
|
||||
totalCalls int
|
||||
|
||||
// Call to this method can be optional
|
||||
optional bool
|
||||
|
||||
// Holds a channel that will be used to block the Return until it either
|
||||
// receives a message or is closed. nil means it returns immediately.
|
||||
WaitFor <-chan time.Time
|
||||
|
||||
waitTime time.Duration
|
||||
|
||||
// Holds a handler used to manipulate arguments content that are passed by
|
||||
// reference. It's useful when mocking methods such as unmarshalers or
|
||||
// decoders.
|
||||
RunFn func(Arguments)
|
||||
}
|
||||
|
||||
func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call {
|
||||
return &Call{
|
||||
Parent: parent,
|
||||
Method: methodName,
|
||||
Arguments: methodArguments,
|
||||
ReturnArguments: make([]interface{}, 0),
|
||||
callerInfo: callerInfo,
|
||||
Repeatability: 0,
|
||||
WaitFor: nil,
|
||||
RunFn: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Call) lock() {
|
||||
c.Parent.mutex.Lock()
|
||||
}
|
||||
|
||||
func (c *Call) unlock() {
|
||||
c.Parent.mutex.Unlock()
|
||||
}
|
||||
|
||||
// Return specifies the return arguments for the expectation.
|
||||
//
|
||||
// Mock.On("DoSomething").Return(errors.New("failed"))
|
||||
func (c *Call) Return(returnArguments ...interface{}) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
|
||||
c.ReturnArguments = returnArguments
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Once indicates that that the mock should only return the value once.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
||||
func (c *Call) Once() *Call {
|
||||
return c.Times(1)
|
||||
}
|
||||
|
||||
// Twice indicates that that the mock should only return the value twice.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
||||
func (c *Call) Twice() *Call {
|
||||
return c.Times(2)
|
||||
}
|
||||
|
||||
// Times indicates that that the mock should only return the indicated number
|
||||
// of times.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
||||
func (c *Call) Times(i int) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
c.Repeatability = i
|
||||
return c
|
||||
}
|
||||
|
||||
// WaitUntil sets the channel that will block the mock's return until its closed
|
||||
// or a message is received.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
|
||||
func (c *Call) WaitUntil(w <-chan time.Time) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
c.WaitFor = w
|
||||
return c
|
||||
}
|
||||
|
||||
// After sets how long to block until the call returns
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
|
||||
func (c *Call) After(d time.Duration) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
c.waitTime = d
|
||||
return c
|
||||
}
|
||||
|
||||
// Run sets a handler to be called before returning. It can be used when
|
||||
// mocking a method such as unmarshalers that takes a pointer to a struct and
|
||||
// sets properties in such struct
|
||||
//
|
||||
// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}").Return().Run(func(args Arguments) {
|
||||
// arg := args.Get(0).(*map[string]interface{})
|
||||
// arg["foo"] = "bar"
|
||||
// })
|
||||
func (c *Call) Run(fn func(args Arguments)) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
c.RunFn = fn
|
||||
return c
|
||||
}
|
||||
|
||||
// Maybe allows the method call to be optional. Not calling an optional method
|
||||
// will not cause an error while asserting expectations
|
||||
func (c *Call) Maybe() *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
c.optional = true
|
||||
return c
|
||||
}
|
||||
|
||||
// On chains a new expectation description onto the mocked interface. This
|
||||
// allows syntax like.
|
||||
//
|
||||
// Mock.
|
||||
// On("MyMethod", 1).Return(nil).
|
||||
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
||||
//go:noinline
|
||||
func (c *Call) On(methodName string, arguments ...interface{}) *Call {
|
||||
return c.Parent.On(methodName, arguments...)
|
||||
}
|
||||
|
||||
// Mock is the workhorse used to track activity on another object.
|
||||
// For an example of its usage, refer to the "Example Usage" section at the top
|
||||
// of this document.
|
||||
type Mock struct {
|
||||
// Represents the calls that are expected of
|
||||
// an object.
|
||||
ExpectedCalls []*Call
|
||||
|
||||
// Holds the calls that were made to this mocked object.
|
||||
Calls []Call
|
||||
|
||||
// test is An optional variable that holds the test struct, to be used when an
|
||||
// invalid mock call was made.
|
||||
test TestingT
|
||||
|
||||
// TestData holds any data that might be useful for testing. Testify ignores
|
||||
// this data completely allowing you to do whatever you like with it.
|
||||
testData objx.Map
|
||||
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// TestData holds any data that might be useful for testing. Testify ignores
|
||||
// this data completely allowing you to do whatever you like with it.
|
||||
func (m *Mock) TestData() objx.Map {
|
||||
|
||||
if m.testData == nil {
|
||||
m.testData = make(objx.Map)
|
||||
}
|
||||
|
||||
return m.testData
|
||||
}
|
||||
|
||||
/*
|
||||
Setting expectations
|
||||
*/
|
||||
|
||||
// Test sets the test struct variable of the mock object
|
||||
func (m *Mock) Test(t TestingT) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
m.test = t
|
||||
}
|
||||
|
||||
// fail fails the current test with the given formatted format and args.
|
||||
// In case that a test was defined, it uses the test APIs for failing a test,
|
||||
// otherwise it uses panic.
|
||||
func (m *Mock) fail(format string, args ...interface{}) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
if m.test == nil {
|
||||
panic(fmt.Sprintf(format, args...))
|
||||
}
|
||||
m.test.Errorf(format, args...)
|
||||
m.test.FailNow()
|
||||
}
|
||||
|
||||
// On starts a description of an expectation of the specified method
|
||||
// being called.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2)
|
||||
func (m *Mock) On(methodName string, arguments ...interface{}) *Call {
|
||||
for _, arg := range arguments {
|
||||
if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
|
||||
panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
|
||||
}
|
||||
}
|
||||
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
c := newCall(m, methodName, assert.CallerInfo(), arguments...)
|
||||
m.ExpectedCalls = append(m.ExpectedCalls, c)
|
||||
return c
|
||||
}
|
||||
|
||||
// /*
|
||||
// Recording and responding to activity
|
||||
// */
|
||||
|
||||
func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) {
|
||||
for i, call := range m.ExpectedCalls {
|
||||
if call.Method == method && call.Repeatability > -1 {
|
||||
|
||||
_, diffCount := call.Arguments.Diff(arguments)
|
||||
if diffCount == 0 {
|
||||
return i, call
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) {
|
||||
var diffCount int
|
||||
var closestCall *Call
|
||||
var err string
|
||||
|
||||
for _, call := range m.expectedCalls() {
|
||||
if call.Method == method {
|
||||
|
||||
errInfo, tempDiffCount := call.Arguments.Diff(arguments)
|
||||
if tempDiffCount < diffCount || diffCount == 0 {
|
||||
diffCount = tempDiffCount
|
||||
closestCall = call
|
||||
err = errInfo
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return closestCall, err
|
||||
}
|
||||
|
||||
func callString(method string, arguments Arguments, includeArgumentValues bool) string {
|
||||
|
||||
var argValsString string
|
||||
if includeArgumentValues {
|
||||
var argVals []string
|
||||
for argIndex, arg := range arguments {
|
||||
argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg))
|
||||
}
|
||||
argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t"))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString)
|
||||
}
|
||||
|
||||
// Called tells the mock object that a method has been called, and gets an array
|
||||
// of arguments to return. Panics if the call is unexpected (i.e. not preceded by
|
||||
// appropriate .On .Return() calls)
|
||||
// If Call.WaitFor is set, blocks until the channel is closed or receives a message.
|
||||
func (m *Mock) Called(arguments ...interface{}) Arguments {
|
||||
// get the calling function's name
|
||||
pc, _, _, ok := runtime.Caller(1)
|
||||
if !ok {
|
||||
panic("Couldn't get the caller information")
|
||||
}
|
||||
functionPath := runtime.FuncForPC(pc).Name()
|
||||
//Next four lines are required to use GCCGO function naming conventions.
|
||||
//For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock
|
||||
//uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree
|
||||
//With GCCGO we need to remove interface information starting from pN<dd>.
|
||||
re := regexp.MustCompile("\\.pN\\d+_")
|
||||
if re.MatchString(functionPath) {
|
||||
functionPath = re.Split(functionPath, -1)[0]
|
||||
}
|
||||
parts := strings.Split(functionPath, ".")
|
||||
functionName := parts[len(parts)-1]
|
||||
return m.MethodCalled(functionName, arguments...)
|
||||
}
|
||||
|
||||
// MethodCalled tells the mock object that the given method has been called, and gets
|
||||
// an array of arguments to return. Panics if the call is unexpected (i.e. not preceded
|
||||
// by appropriate .On .Return() calls)
|
||||
// If Call.WaitFor is set, blocks until the channel is closed or receives a message.
|
||||
func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments {
|
||||
m.mutex.Lock()
|
||||
//TODO: could combine expected and closes in single loop
|
||||
found, call := m.findExpectedCall(methodName, arguments...)
|
||||
|
||||
if found < 0 {
|
||||
// we have to fail here - because we don't know what to do
|
||||
// as the return arguments. This is because:
|
||||
//
|
||||
// a) this is a totally unexpected call to this method,
|
||||
// b) the arguments are not what was expected, or
|
||||
// c) the developer has forgotten to add an accompanying On...Return pair.
|
||||
|
||||
closestCall, mismatch := m.findClosestCall(methodName, arguments...)
|
||||
m.mutex.Unlock()
|
||||
|
||||
if closestCall != nil {
|
||||
m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s",
|
||||
callString(methodName, arguments, true),
|
||||
callString(methodName, closestCall.Arguments, true),
|
||||
diffArguments(closestCall.Arguments, arguments),
|
||||
strings.TrimSpace(mismatch),
|
||||
)
|
||||
} else {
|
||||
m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo())
|
||||
}
|
||||
}
|
||||
|
||||
if call.Repeatability == 1 {
|
||||
call.Repeatability = -1
|
||||
} else if call.Repeatability > 1 {
|
||||
call.Repeatability--
|
||||
}
|
||||
call.totalCalls++
|
||||
|
||||
// add the call
|
||||
m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...))
|
||||
m.mutex.Unlock()
|
||||
|
||||
// block if specified
|
||||
if call.WaitFor != nil {
|
||||
<-call.WaitFor
|
||||
} else {
|
||||
time.Sleep(call.waitTime)
|
||||
}
|
||||
|
||||
m.mutex.Lock()
|
||||
runFn := call.RunFn
|
||||
m.mutex.Unlock()
|
||||
|
||||
if runFn != nil {
|
||||
runFn(arguments)
|
||||
}
|
||||
|
||||
m.mutex.Lock()
|
||||
returnArgs := call.ReturnArguments
|
||||
m.mutex.Unlock()
|
||||
|
||||
return returnArgs
|
||||
}
|
||||
|
||||
/*
|
||||
Assertions
|
||||
*/
|
||||
|
||||
type assertExpectationser interface {
|
||||
AssertExpectations(TestingT) bool
|
||||
}
|
||||
|
||||
// AssertExpectationsForObjects asserts that everything specified with On and Return
|
||||
// of the specified objects was in fact called as expected.
|
||||
//
|
||||
// Calls may have occurred in any order.
|
||||
func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
for _, obj := range testObjects {
|
||||
if m, ok := obj.(Mock); ok {
|
||||
t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)")
|
||||
obj = &m
|
||||
}
|
||||
m := obj.(assertExpectationser)
|
||||
if !m.AssertExpectations(t) {
|
||||
t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AssertExpectations asserts that everything specified with On and Return was
|
||||
// in fact called as expected. Calls may have occurred in any order.
|
||||
func (m *Mock) AssertExpectations(t TestingT) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
var somethingMissing bool
|
||||
var failedExpectations int
|
||||
|
||||
// iterate through each expectation
|
||||
expectedCalls := m.expectedCalls()
|
||||
for _, expectedCall := range expectedCalls {
|
||||
if !expectedCall.optional && !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments) && expectedCall.totalCalls == 0 {
|
||||
somethingMissing = true
|
||||
failedExpectations++
|
||||
t.Logf("FAIL:\t%s(%s)\n\t\tat: %s", expectedCall.Method, expectedCall.Arguments.String(), expectedCall.callerInfo)
|
||||
} else {
|
||||
if expectedCall.Repeatability > 0 {
|
||||
somethingMissing = true
|
||||
failedExpectations++
|
||||
t.Logf("FAIL:\t%s(%s)\n\t\tat: %s", expectedCall.Method, expectedCall.Arguments.String(), expectedCall.callerInfo)
|
||||
} else {
|
||||
t.Logf("PASS:\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if somethingMissing {
|
||||
t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo())
|
||||
}
|
||||
|
||||
return !somethingMissing
|
||||
}
|
||||
|
||||
// AssertNumberOfCalls asserts that the method was called expectedCalls times.
|
||||
func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
var actualCalls int
|
||||
for _, call := range m.calls() {
|
||||
if call.Method == methodName {
|
||||
actualCalls++
|
||||
}
|
||||
}
|
||||
return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls))
|
||||
}
|
||||
|
||||
// AssertCalled asserts that the method was called.
|
||||
// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method.
|
||||
func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
if !m.methodWasCalled(methodName, arguments) {
|
||||
var calledWithArgs []string
|
||||
for _, call := range m.calls() {
|
||||
calledWithArgs = append(calledWithArgs, fmt.Sprintf("%v", call.Arguments))
|
||||
}
|
||||
if len(calledWithArgs) == 0 {
|
||||
return assert.Fail(t, "Should have called with given arguments",
|
||||
fmt.Sprintf("Expected %q to have been called with:\n%v\nbut no actual calls happened", methodName, arguments))
|
||||
}
|
||||
return assert.Fail(t, "Should have called with given arguments",
|
||||
fmt.Sprintf("Expected %q to have been called with:\n%v\nbut actual calls were:\n %v", methodName, arguments, strings.Join(calledWithArgs, "\n")))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AssertNotCalled asserts that the method was not called.
|
||||
// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method.
|
||||
func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
if m.methodWasCalled(methodName, arguments) {
|
||||
return assert.Fail(t, "Should not have called with given arguments",
|
||||
fmt.Sprintf("Expected %q to not have been called with:\n%v\nbut actually it was.", methodName, arguments))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool {
|
||||
for _, call := range m.calls() {
|
||||
if call.Method == methodName {
|
||||
|
||||
_, differences := Arguments(expected).Diff(call.Arguments)
|
||||
|
||||
if differences == 0 {
|
||||
// found the expected call
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// we didn't find the expected call
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Mock) expectedCalls() []*Call {
|
||||
return append([]*Call{}, m.ExpectedCalls...)
|
||||
}
|
||||
|
||||
func (m *Mock) calls() []Call {
|
||||
return append([]Call{}, m.Calls...)
|
||||
}
|
||||
|
||||
/*
|
||||
Arguments
|
||||
*/
|
||||
|
||||
// Arguments holds an array of method arguments or return values.
|
||||
type Arguments []interface{}
|
||||
|
||||
const (
|
||||
// Anything is used in Diff and Assert when the argument being tested
|
||||
// shouldn't be taken into consideration.
|
||||
Anything = "mock.Anything"
|
||||
)
|
||||
|
||||
// AnythingOfTypeArgument is a string that contains the type of an argument
|
||||
// for use when type checking. Used in Diff and Assert.
|
||||
type AnythingOfTypeArgument string
|
||||
|
||||
// AnythingOfType returns an AnythingOfTypeArgument object containing the
|
||||
// name of the type to check for. Used in Diff and Assert.
|
||||
//
|
||||
// For example:
|
||||
// Assert(t, AnythingOfType("string"), AnythingOfType("int"))
|
||||
func AnythingOfType(t string) AnythingOfTypeArgument {
|
||||
return AnythingOfTypeArgument(t)
|
||||
}
|
||||
|
||||
// argumentMatcher performs custom argument matching, returning whether or
|
||||
// not the argument is matched by the expectation fixture function.
|
||||
type argumentMatcher struct {
|
||||
// fn is a function which accepts one argument, and returns a bool.
|
||||
fn reflect.Value
|
||||
}
|
||||
|
||||
func (f argumentMatcher) Matches(argument interface{}) bool {
|
||||
expectType := f.fn.Type().In(0)
|
||||
expectTypeNilSupported := false
|
||||
switch expectType.Kind() {
|
||||
case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Ptr:
|
||||
expectTypeNilSupported = true
|
||||
}
|
||||
|
||||
argType := reflect.TypeOf(argument)
|
||||
var arg reflect.Value
|
||||
if argType == nil {
|
||||
arg = reflect.New(expectType).Elem()
|
||||
} else {
|
||||
arg = reflect.ValueOf(argument)
|
||||
}
|
||||
|
||||
if argType == nil && !expectTypeNilSupported {
|
||||
panic(errors.New("attempting to call matcher with nil for non-nil expected type"))
|
||||
}
|
||||
if argType == nil || argType.AssignableTo(expectType) {
|
||||
result := f.fn.Call([]reflect.Value{arg})
|
||||
return result[0].Bool()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f argumentMatcher) String() string {
|
||||
return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).Name())
|
||||
}
|
||||
|
||||
// MatchedBy can be used to match a mock call based on only certain properties
|
||||
// from a complex struct or some calculation. It takes a function that will be
|
||||
// evaluated with the called argument and will return true when there's a match
|
||||
// and false otherwise.
|
||||
//
|
||||
// Example:
|
||||
// m.On("Do", MatchedBy(func(req *http.Request) bool { return req.Host == "example.com" }))
|
||||
//
|
||||
// |fn|, must be a function accepting a single argument (of the expected type)
|
||||
// which returns a bool. If |fn| doesn't match the required signature,
|
||||
// MatchedBy() panics.
|
||||
func MatchedBy(fn interface{}) argumentMatcher {
|
||||
fnType := reflect.TypeOf(fn)
|
||||
|
||||
if fnType.Kind() != reflect.Func {
|
||||
panic(fmt.Sprintf("assert: arguments: %s is not a func", fn))
|
||||
}
|
||||
if fnType.NumIn() != 1 {
|
||||
panic(fmt.Sprintf("assert: arguments: %s does not take exactly one argument", fn))
|
||||
}
|
||||
if fnType.NumOut() != 1 || fnType.Out(0).Kind() != reflect.Bool {
|
||||
panic(fmt.Sprintf("assert: arguments: %s does not return a bool", fn))
|
||||
}
|
||||
|
||||
return argumentMatcher{fn: reflect.ValueOf(fn)}
|
||||
}
|
||||
|
||||
// Get Returns the argument at the specified index.
|
||||
func (args Arguments) Get(index int) interface{} {
|
||||
if index+1 > len(args) {
|
||||
panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args)))
|
||||
}
|
||||
return args[index]
|
||||
}
|
||||
|
||||
// Is gets whether the objects match the arguments specified.
|
||||
func (args Arguments) Is(objects ...interface{}) bool {
|
||||
for i, obj := range args {
|
||||
if obj != objects[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Diff gets a string describing the differences between the arguments
|
||||
// and the specified objects.
|
||||
//
|
||||
// Returns the diff string and number of differences found.
|
||||
func (args Arguments) Diff(objects []interface{}) (string, int) {
|
||||
//TODO: could return string as error and nil for No difference
|
||||
|
||||
var output = "\n"
|
||||
var differences int
|
||||
|
||||
var maxArgCount = len(args)
|
||||
if len(objects) > maxArgCount {
|
||||
maxArgCount = len(objects)
|
||||
}
|
||||
|
||||
for i := 0; i < maxArgCount; i++ {
|
||||
var actual, expected interface{}
|
||||
var actualFmt, expectedFmt string
|
||||
|
||||
if len(objects) <= i {
|
||||
actual = "(Missing)"
|
||||
actualFmt = "(Missing)"
|
||||
} else {
|
||||
actual = objects[i]
|
||||
actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual)
|
||||
}
|
||||
|
||||
if len(args) <= i {
|
||||
expected = "(Missing)"
|
||||
expectedFmt = "(Missing)"
|
||||
} else {
|
||||
expected = args[i]
|
||||
expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected)
|
||||
}
|
||||
|
||||
if matcher, ok := expected.(argumentMatcher); ok {
|
||||
if matcher.Matches(actual) {
|
||||
output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher)
|
||||
} else {
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher)
|
||||
}
|
||||
} else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() {
|
||||
|
||||
// type checking
|
||||
if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) {
|
||||
// not match
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// normal checking
|
||||
|
||||
if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
|
||||
// match
|
||||
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt)
|
||||
} else {
|
||||
// not match
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if differences == 0 {
|
||||
return "No differences.", differences
|
||||
}
|
||||
|
||||
return output, differences
|
||||
|
||||
}
|
||||
|
||||
// Assert compares the arguments with the specified objects and fails if
|
||||
// they do not exactly match.
|
||||
func (args Arguments) Assert(t TestingT, objects ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
// get the differences
|
||||
diff, diffCount := args.Diff(objects)
|
||||
|
||||
if diffCount == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// there are differences... report them...
|
||||
t.Logf(diff)
|
||||
t.Errorf("%sArguments do not match.", assert.CallerInfo())
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
// String gets the argument at the specified index. Panics if there is no argument, or
|
||||
// if the argument is of the wrong type.
|
||||
//
|
||||
// If no index is provided, String() returns a complete string representation
|
||||
// of the arguments.
|
||||
func (args Arguments) String(indexOrNil ...int) string {
|
||||
|
||||
if len(indexOrNil) == 0 {
|
||||
// normal String() method - return a string representation of the args
|
||||
var argsStr []string
|
||||
for _, arg := range args {
|
||||
argsStr = append(argsStr, fmt.Sprintf("%s", reflect.TypeOf(arg)))
|
||||
}
|
||||
return strings.Join(argsStr, ",")
|
||||
} else if len(indexOrNil) == 1 {
|
||||
// Index has been specified - get the argument at that index
|
||||
var index = indexOrNil[0]
|
||||
var s string
|
||||
var ok bool
|
||||
if s, ok = args.Get(index).(string); !ok {
|
||||
panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index)))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil)))
|
||||
|
||||
}
|
||||
|
||||
// Int gets the argument at the specified index. Panics if there is no argument, or
|
||||
// if the argument is of the wrong type.
|
||||
func (args Arguments) Int(index int) int {
|
||||
var s int
|
||||
var ok bool
|
||||
if s, ok = args.Get(index).(int); !ok {
|
||||
panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Error gets the argument at the specified index. Panics if there is no argument, or
|
||||
// if the argument is of the wrong type.
|
||||
func (args Arguments) Error(index int) error {
|
||||
obj := args.Get(index)
|
||||
var s error
|
||||
var ok bool
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
if s, ok = obj.(error); !ok {
|
||||
panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Bool gets the argument at the specified index. Panics if there is no argument, or
|
||||
// if the argument is of the wrong type.
|
||||
func (args Arguments) Bool(index int) bool {
|
||||
var s bool
|
||||
var ok bool
|
||||
if s, ok = args.Get(index).(bool); !ok {
|
||||
panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
|
||||
t := reflect.TypeOf(v)
|
||||
k := t.Kind()
|
||||
|
||||
if k == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
k = t.Kind()
|
||||
}
|
||||
return t, k
|
||||
}
|
||||
|
||||
func diffArguments(expected Arguments, actual Arguments) string {
|
||||
if len(expected) != len(actual) {
|
||||
return fmt.Sprintf("Provided %v arguments, mocked for %v arguments", len(expected), len(actual))
|
||||
}
|
||||
|
||||
for x := range expected {
|
||||
if diffString := diff(expected[x], actual[x]); diffString != "" {
|
||||
return fmt.Sprintf("Difference found in argument %v:\n\n%s", x, diffString)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// diff returns a diff of both values as long as both are of the same type and
|
||||
// are a struct, map, slice or array. Otherwise it returns an empty string.
|
||||
func diff(expected interface{}, actual interface{}) string {
|
||||
if expected == nil || actual == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
et, ek := typeAndKind(expected)
|
||||
at, _ := typeAndKind(actual)
|
||||
|
||||
if et != at {
|
||||
return ""
|
||||
}
|
||||
|
||||
if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array {
|
||||
return ""
|
||||
}
|
||||
|
||||
e := spewConfig.Sdump(expected)
|
||||
a := spewConfig.Sdump(actual)
|
||||
|
||||
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(e),
|
||||
B: difflib.SplitLines(a),
|
||||
FromFile: "Expected",
|
||||
FromDate: "",
|
||||
ToFile: "Actual",
|
||||
ToDate: "",
|
||||
Context: 1,
|
||||
})
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
var spewConfig = spew.ConfigState{
|
||||
Indent: " ",
|
||||
DisablePointerAddresses: true,
|
||||
DisableCapacities: true,
|
||||
SortKeys: true,
|
||||
}
|
||||
|
||||
type tHelper interface {
|
||||
Helper()
|
||||
}
|
852
src/vendor/github.com/stretchr/testify/require/require.go
generated
vendored
852
src/vendor/github.com/stretchr/testify/require/require.go
generated
vendored
File diff suppressed because it is too large
Load Diff
6
src/vendor/github.com/stretchr/testify/require/require.go.tmpl
generated
vendored
6
src/vendor/github.com/stretchr/testify/require/require.go.tmpl
generated
vendored
@ -1,6 +1,6 @@
|
||||
{{.Comment}}
|
||||
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
|
||||
if !assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) {
|
||||
t.FailNow()
|
||||
}
|
||||
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return }
|
||||
if h, ok := t.(tHelper); ok { h.Helper() }
|
||||
t.FailNow()
|
||||
}
|
||||
|
402
src/vendor/github.com/stretchr/testify/require/require_forward.go
generated
vendored
402
src/vendor/github.com/stretchr/testify/require/require_forward.go
generated
vendored
@ -14,11 +14,17 @@ import (
|
||||
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -28,9 +34,10 @@ func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...inte
|
||||
// a.Contains("Hello World", "World")
|
||||
// a.Contains(["Hello", "World"], "World")
|
||||
// a.Contains({"Hello": "World"}, "Hello")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -40,19 +47,26 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ..
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -60,10 +74,11 @@ func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) {
|
||||
// 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]))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -71,10 +86,11 @@ func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndA
|
||||
// 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"))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -82,9 +98,10 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// a.Empty(obj)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
Empty(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -92,9 +109,10 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) {
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// a.Emptyf(obj, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -102,12 +120,13 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{})
|
||||
//
|
||||
// a.Equal(123, 123)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -116,9 +135,10 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// a.EqualError(err, expectedErrorString)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -127,9 +147,10 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -137,9 +158,10 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a
|
||||
// and equal.
|
||||
//
|
||||
// a.EqualValues(uint32(123), int32(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -147,9 +169,10 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
|
||||
// and equal.
|
||||
//
|
||||
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -157,12 +180,13 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg
|
||||
//
|
||||
// a.Equalf(123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -172,9 +196,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string
|
||||
// if a.Error(err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
Error(a.t, err, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -184,115 +209,150 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) {
|
||||
// if a.Errorf(err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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))
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// 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, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// 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, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// 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, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -302,6 +362,9 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -311,6 +374,9 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -320,6 +386,9 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -329,6 +398,9 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -338,6 +410,9 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -347,6 +422,9 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -354,6 +432,9 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -361,96 +442,129 @@ func (a *Assertions) Implements(interfaceObject interface{}, object 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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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"}`)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -458,9 +572,10 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ..
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// a.Len(mySlice, 3)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -468,27 +583,30 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// a.Lenf(mySlice, 3, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -498,9 +616,10 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) {
|
||||
// if a.NoError(err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
NoError(a.t, err, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -510,9 +629,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) {
|
||||
// if a.NoErrorf(err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -522,9 +642,10 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) {
|
||||
// a.NotContains("Hello World", "Earth")
|
||||
// a.NotContains(["Hello", "World"], "Earth")
|
||||
// a.NotContains({"Hello": "World"}, "Earth")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -534,9 +655,10 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -546,9 +668,10 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin
|
||||
// if a.NotEmpty(obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
NotEmpty(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -558,9 +681,10 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) {
|
||||
// if a.NotEmptyf(obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -568,11 +692,12 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface
|
||||
//
|
||||
// a.NotEqual(obj1, obj2)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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...)
|
||||
}
|
||||
|
||||
@ -580,47 +705,52 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr
|
||||
//
|
||||
// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -628,9 +758,10 @@ func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...inte
|
||||
//
|
||||
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
|
||||
// a.NotRegexp("^start", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -638,9 +769,10 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in
|
||||
//
|
||||
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
|
||||
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -648,9 +780,10 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -658,28 +791,36 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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 and returns the truth.
|
||||
// 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 and returns the truth.
|
||||
// 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() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
Panics(a.t, f, msgAndArgs...)
|
||||
}
|
||||
|
||||
@ -687,9 +828,10 @@ func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) {
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// a.PanicsWithValue("crazy error", func(){ GoCrazy() })
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -697,18 +839,20 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFun
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -716,9 +860,10 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa
|
||||
//
|
||||
// a.Regexp(regexp.MustCompile("start"), "it's starting")
|
||||
// a.Regexp("start...$", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -726,9 +871,10 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
|
||||
//
|
||||
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
|
||||
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -736,9 +882,10 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args .
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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...)
|
||||
}
|
||||
|
||||
@ -746,54 +893,65 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
|
||||
// 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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
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 and returns the truth.
|
||||
// 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 and returns the truth.
|
||||
// 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...)
|
||||
}
|
||||
|
1
src/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl
generated
vendored
1
src/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl
generated
vendored
@ -1,4 +1,5 @@
|
||||
{{.CommentWithoutT "a"}}
|
||||
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) {
|
||||
if h, ok := a.t.(tHelper); ok { h.Helper() }
|
||||
{{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
|
||||
}
|
||||
|
20
src/vendor/github.com/stretchr/testify/require/requirements.go
generated
vendored
20
src/vendor/github.com/stretchr/testify/require/requirements.go
generated
vendored
@ -6,4 +6,24 @@ type TestingT 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{})
|
||||
|
||||
// ErrorAssertionFunc 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
|
||||
|
24
src/vendor/github.com/stretchr/testify/suite/suite.go
generated
vendored
24
src/vendor/github.com/stretchr/testify/suite/suite.go
generated
vendored
@ -55,10 +55,32 @@ func (suite *Suite) Assert() *assert.Assertions {
|
||||
return suite.Assertions
|
||||
}
|
||||
|
||||
func failOnPanic(t *testing.T) {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
t.Errorf("test panicked: %v", r)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// Run provides suite functionality around golang subtests. It should be
|
||||
// called in place of t.Run(name, func(t *testing.T)) in test suite code.
|
||||
// The passed-in func will be executed as a subtest with a fresh instance of t.
|
||||
// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName.
|
||||
func (suite *Suite) Run(name string, subtest func()) bool {
|
||||
oldT := suite.T()
|
||||
defer suite.SetT(oldT)
|
||||
return oldT.Run(name, func(t *testing.T) {
|
||||
suite.SetT(t)
|
||||
subtest()
|
||||
})
|
||||
}
|
||||
|
||||
// Run takes a testing suite and runs all of the tests attached
|
||||
// to it.
|
||||
func Run(t *testing.T, suite TestingSuite) {
|
||||
suite.SetT(t)
|
||||
defer failOnPanic(t)
|
||||
|
||||
if setupAllSuite, ok := suite.(SetupAllSuite); ok {
|
||||
setupAllSuite.SetupSuite()
|
||||
@ -84,6 +106,8 @@ func Run(t *testing.T, suite TestingSuite) {
|
||||
F: func(t *testing.T) {
|
||||
parentT := suite.T()
|
||||
suite.SetT(t)
|
||||
defer failOnPanic(t)
|
||||
|
||||
if setupTestSuite, ok := suite.(SetupTestSuite); ok {
|
||||
setupTestSuite.SetupTest()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user