mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-21 16:55:38 +03:00
[chore] Test fixes (#788)
* use 'test' value for testrig storage backend * update test dependency * add WaitFor func in testrig * use WaitFor function instead of time.Sleep * tidy up tests * make SentMessages a sync.map * go fmt
This commit is contained in:
parent
bee8458a2d
commit
0245c606d7
30 changed files with 501 additions and 222 deletions
4
go.mod
4
go.mod
|
@ -37,7 +37,7 @@ require (
|
|||
github.com/russross/blackfriday/v2 v2.1.0
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/viper v1.11.0
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/superseriousbusiness/activity v1.1.0-gts
|
||||
github.com/superseriousbusiness/exif-terminator v0.4.0
|
||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB
|
||||
|
@ -138,7 +138,7 @@ require (
|
|||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.36.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.8 // indirect
|
||||
|
|
7
go.sum
7
go.sum
|
@ -499,14 +499,16 @@ github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUs
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/superseriousbusiness/activity v1.1.0-gts h1:BSnMzs/84s0Zme7BngE9iJAHV7g1Bv1nhLCP0aJtU3I=
|
||||
|
@ -987,8 +989,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type MediaCleanupTestSuite struct {
|
||||
|
@ -47,15 +48,15 @@ func (suite *MediaCleanupTestSuite) TestMediaCleanup() {
|
|||
// we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
||||
// Wait for async task to finish
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Get media we prunes
|
||||
prunedAttachment, err := suite.db.GetAttachmentByID(context.Background(), testAttachment.ID)
|
||||
suite.NoError(err)
|
||||
|
||||
// the media should no longer be cached
|
||||
suite.False(*prunedAttachment.Cached)
|
||||
// the attachment should be updated in the database
|
||||
if !testrig.WaitFor(func() bool {
|
||||
if prunedAttachment, _ := suite.db.GetAttachmentByID(context.Background(), testAttachment.ID); prunedAttachment != nil {
|
||||
return !*prunedAttachment.Cached
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for attachment to be pruned")
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *MediaCleanupTestSuite) TestMediaCleanupNoArg() {
|
||||
|
@ -73,15 +74,14 @@ func (suite *MediaCleanupTestSuite) TestMediaCleanupNoArg() {
|
|||
// we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
||||
// Wait for async task to finish
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Get media we prunes
|
||||
prunedAttachment, err := suite.db.GetAttachmentByID(context.Background(), testAttachment.ID)
|
||||
suite.NoError(err)
|
||||
|
||||
// the media should no longer be cached
|
||||
suite.False(*prunedAttachment.Cached)
|
||||
if !testrig.WaitFor(func() bool {
|
||||
if prunedAttachment, _ := suite.db.GetAttachmentByID(context.Background(), testAttachment.ID); prunedAttachment != nil {
|
||||
return !*prunedAttachment.Cached
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for attachment to be pruned")
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *MediaCleanupTestSuite) TestMediaCleanupNotOldEnough() {
|
||||
|
@ -101,7 +101,7 @@ func (suite *MediaCleanupTestSuite) TestMediaCleanupNotOldEnough() {
|
|||
// Wait for async task to finish
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Get media we prunes
|
||||
// Get media we pruned
|
||||
prunedAttachment, err := suite.db.GetAttachmentByID(context.Background(), testAttachment.ID)
|
||||
suite.NoError(err)
|
||||
|
||||
|
|
|
@ -444,14 +444,15 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
|
|||
suite.Empty(b)
|
||||
suite.Equal(http.StatusOK, result.StatusCode)
|
||||
|
||||
// sleep for a sec so side effects can process in the background
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// local account 2 blocked foss_satan, that block should be gone now
|
||||
testBlock := suite.testBlocks["local_account_2_block_remote_account_1"]
|
||||
dbBlock := >smodel.Block{}
|
||||
err = suite.db.GetByID(ctx, testBlock.ID, dbBlock)
|
||||
suite.ErrorIs(err, db.ErrNoEntries)
|
||||
if !testrig.WaitFor(func() bool {
|
||||
// local account 2 blocked foss_satan, that block should be gone now
|
||||
testBlock := suite.testBlocks["local_account_2_block_remote_account_1"]
|
||||
dbBlock := >smodel.Block{}
|
||||
err = suite.db.GetByID(ctx, testBlock.ID, dbBlock)
|
||||
return suite.ErrorIs(err, db.ErrNoEntries)
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for block to be removed")
|
||||
}
|
||||
|
||||
// no statuses from foss satan should be left in the database
|
||||
dbStatuses, err := suite.db.GetAccountStatuses(ctx, deletedAccount.ID, 0, false, false, "", "", false, false, false)
|
||||
|
|
|
@ -46,15 +46,6 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
|
|||
signedRequest := derefRequests["foss_satan_dereference_zork_outbox"]
|
||||
targetAccount := suite.testAccounts["local_account_1"]
|
||||
|
||||
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
|
||||
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
||||
|
||||
tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker)
|
||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
|
||||
// setup request
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||
|
@ -76,7 +67,7 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
|
|||
}
|
||||
|
||||
// trigger the function being tested
|
||||
userModule.OutboxGETHandler(ctx)
|
||||
suite.userModule.OutboxGETHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
|
|
@ -49,15 +49,6 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
|
|||
targetAccount := suite.testAccounts["local_account_1"]
|
||||
targetStatus := suite.testStatuses["local_account_1_status_1"]
|
||||
|
||||
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
|
||||
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
||||
|
||||
tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker)
|
||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
|
||||
// setup request
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||
|
@ -83,7 +74,7 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
|
|||
}
|
||||
|
||||
// trigger the function being tested
|
||||
userModule.StatusRepliesGETHandler(ctx)
|
||||
suite.userModule.StatusRepliesGETHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
|
|
@ -32,8 +32,6 @@ import (
|
|||
"github.com/superseriousbusiness/activity/streams"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
@ -48,15 +46,6 @@ func (suite *StatusGetTestSuite) TestGetStatus() {
|
|||
targetAccount := suite.testAccounts["local_account_1"]
|
||||
targetStatus := suite.testStatuses["local_account_1_status_1"]
|
||||
|
||||
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
|
||||
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
||||
|
||||
tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker)
|
||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
|
||||
// setup request
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||
|
@ -82,7 +71,7 @@ func (suite *StatusGetTestSuite) TestGetStatus() {
|
|||
}
|
||||
|
||||
// trigger the function being tested
|
||||
userModule.StatusGETHandler(ctx)
|
||||
suite.userModule.StatusGETHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
@ -116,15 +105,6 @@ func (suite *StatusGetTestSuite) TestGetStatusLowercase() {
|
|||
targetAccount := suite.testAccounts["local_account_1"]
|
||||
targetStatus := suite.testStatuses["local_account_1_status_1"]
|
||||
|
||||
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
|
||||
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
||||
|
||||
tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker)
|
||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
|
||||
// setup request
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||
|
@ -150,7 +130,7 @@ func (suite *StatusGetTestSuite) TestGetStatusLowercase() {
|
|||
}
|
||||
|
||||
// trigger the function being tested
|
||||
userModule.StatusGETHandler(ctx)
|
||||
suite.userModule.StatusGETHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
@ -33,8 +32,6 @@ import (
|
|||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
@ -49,15 +46,6 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
|||
signedRequest := derefRequests["foss_satan_dereference_zork"]
|
||||
targetAccount := suite.testAccounts["local_account_1"]
|
||||
|
||||
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
|
||||
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
||||
|
||||
tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker)
|
||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
|
||||
// setup request
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||
|
@ -79,7 +67,7 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
|||
}
|
||||
|
||||
// trigger the function being tested
|
||||
userModule.UsersGETHandler(ctx)
|
||||
suite.userModule.UsersGETHandler(ctx)
|
||||
|
||||
// check response
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
@ -110,6 +98,12 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
|||
// TestGetUserPublicKeyDeleted checks whether the public key of a deleted account can still be dereferenced.
|
||||
// This is needed by remote instances for authenticating delete requests and stuff like that.
|
||||
func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
|
||||
if err := suite.processor.Start(); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
defer suite.processor.Stop()
|
||||
|
||||
userModule := user.New(suite.processor).(*user.Module)
|
||||
targetAccount := suite.testAccounts["local_account_1"]
|
||||
|
||||
// first delete the account, as though zork had deleted himself
|
||||
|
@ -123,22 +117,18 @@ func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
|
|||
DeleteOriginID: targetAccount.ID,
|
||||
})
|
||||
|
||||
// now wait just a sec for it to go through....
|
||||
time.Sleep(1 * time.Second)
|
||||
// wait for the account delete to be processed
|
||||
if !testrig.WaitFor(func() bool {
|
||||
a, _ := suite.db.GetAccountByID(context.Background(), targetAccount.ID)
|
||||
return !a.SuspendedAt.IsZero()
|
||||
}) {
|
||||
suite.FailNow("delete of account timed out")
|
||||
}
|
||||
|
||||
// the dereference we're gonna use
|
||||
derefRequests := testrig.NewTestDereferenceRequests(suite.testAccounts)
|
||||
signedRequest := derefRequests["foss_satan_dereference_zork_public_key"]
|
||||
|
||||
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
|
||||
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
||||
|
||||
tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker)
|
||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
|
||||
// setup request
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||
|
|
|
@ -20,13 +20,12 @@ package dereferencing_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type AttachmentTestSuite struct {
|
||||
|
@ -128,12 +127,11 @@ func (suite *AttachmentTestSuite) TestDereferenceAttachmentAsync() {
|
|||
suite.NoError(err)
|
||||
attachmentID := processingMedia.AttachmentID()
|
||||
|
||||
// wait for the media to finish processing
|
||||
for finished := processingMedia.Finished(); !finished; finished = processingMedia.Finished() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
fmt.Printf("\n\nnot finished yet...\n\n")
|
||||
if !testrig.WaitFor(func() bool {
|
||||
return processingMedia.Finished()
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for media to be processed")
|
||||
}
|
||||
fmt.Printf("\n\nfinished!\n\n")
|
||||
|
||||
// now get the attachment from the database
|
||||
attachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
|
||||
|
|
|
@ -115,10 +115,22 @@ func (suite *FederatingActorTestSuite) TestSendRemoteFollower() {
|
|||
suite.NotNil(activity)
|
||||
|
||||
// because we added 1 remote follower for zork, there should be a url in sentMessage
|
||||
suite.Len(httpClient.SentMessages, 1)
|
||||
msg, ok := httpClient.SentMessages[testRemoteAccount.InboxURI]
|
||||
suite.True(ok)
|
||||
suite.Equal(`{"@context":"https://www.w3.org/ns/activitystreams","actor":"http://localhost:8080/users/the_mighty_zork","id":"http://localhost:8080/whatever_some_create","object":{"attributedTo":"http://localhost:8080/users/the_mighty_zork","content":"boobies","id":"http://localhost:8080/users/the_mighty_zork/statuses/01G1TR6BADACCZWQMNF9X21TV5","published":"2022-06-02T12:22:21+02:00","tag":[],"to":"http://localhost:8080/users/the_mighty_zork/followers","type":"Note","url":"http://localhost:8080/@the_mighty_zork/statuses/01G1TR6BADACCZWQMNF9X21TV5"},"published":"2022-06-02T12:22:21+02:00","to":"http://localhost:8080/users/the_mighty_zork/followers","type":"Create"}`, string(msg))
|
||||
var sent [][]byte
|
||||
if !testrig.WaitFor(func() bool {
|
||||
sentI, ok := httpClient.SentMessages.Load(testRemoteAccount.InboxURI)
|
||||
if ok {
|
||||
sent, ok = sentI.([][]byte)
|
||||
if !ok {
|
||||
panic("SentMessages entry was not []byte")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for message")
|
||||
}
|
||||
|
||||
suite.Equal(`{"@context":"https://www.w3.org/ns/activitystreams","actor":"http://localhost:8080/users/the_mighty_zork","id":"http://localhost:8080/whatever_some_create","object":{"attributedTo":"http://localhost:8080/users/the_mighty_zork","content":"boobies","id":"http://localhost:8080/users/the_mighty_zork/statuses/01G1TR6BADACCZWQMNF9X21TV5","published":"2022-06-02T12:22:21+02:00","tag":[],"to":"http://localhost:8080/users/the_mighty_zork/followers","type":"Note","url":"http://localhost:8080/@the_mighty_zork/statuses/01G1TR6BADACCZWQMNF9X21TV5"},"published":"2022-06-02T12:22:21+02:00","to":"http://localhost:8080/users/the_mighty_zork/followers","type":"Create"}`, string(sent[0]))
|
||||
}
|
||||
|
||||
func TestFederatingActorTestSuite(t *testing.T) {
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gruf/go-store/kv"
|
||||
"codeberg.org/gruf/go-store/storage"
|
||||
|
@ -34,6 +33,7 @@ import (
|
|||
gtsmodel "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type ManagerTestSuite struct {
|
||||
|
@ -360,11 +360,11 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessAsync() {
|
|||
attachmentID := processingMedia.AttachmentID()
|
||||
|
||||
// wait for the media to finish processing
|
||||
for finished := processingMedia.Finished(); !finished; finished = processingMedia.Finished() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
fmt.Printf("\n\nnot finished yet...\n\n")
|
||||
if !testrig.WaitFor(func() bool {
|
||||
return processingMedia.Finished()
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for media to be processed")
|
||||
}
|
||||
fmt.Printf("\n\nfinished!\n\n")
|
||||
|
||||
// fetch the attachment from the database
|
||||
attachment, err := suite.db.GetAttachmentByID(ctx, attachmentID)
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/superseriousbusiness/activity/pub"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type AccountTestSuite struct {
|
||||
|
@ -59,23 +60,30 @@ func (suite *AccountTestSuite) TestAccountDeleteLocal() {
|
|||
suite.NoError(errWithCode)
|
||||
|
||||
// the delete should be federated outwards to the following account's inbox
|
||||
var sent []byte
|
||||
var ok bool
|
||||
for !ok {
|
||||
sent, ok = suite.httpClient.SentMessages[followingAccount.InboxURI]
|
||||
}
|
||||
|
||||
suite.True(ok)
|
||||
delete := &struct {
|
||||
var sent [][]byte
|
||||
delete := new(struct {
|
||||
Actor string `json:"actor"`
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
To string `json:"to"`
|
||||
CC string `json:"cc"`
|
||||
Type string `json:"type"`
|
||||
}{}
|
||||
err = json.Unmarshal(sent, delete)
|
||||
suite.NoError(err)
|
||||
})
|
||||
|
||||
if !testrig.WaitFor(func() bool {
|
||||
sentI, ok := suite.httpClient.SentMessages.Load(followingAccount.InboxURI)
|
||||
if ok {
|
||||
sent, ok = sentI.([][]byte)
|
||||
if !ok {
|
||||
panic("SentMessages entry was not [][]byte")
|
||||
}
|
||||
err = json.Unmarshal(sent[0], delete)
|
||||
return err == nil
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for message")
|
||||
}
|
||||
|
||||
suite.Equal(deletingAccount.URI, delete.Actor)
|
||||
suite.Equal(deletingAccount.URI, delete.Object)
|
||||
|
@ -83,13 +91,12 @@ func (suite *AccountTestSuite) TestAccountDeleteLocal() {
|
|||
suite.Equal(pub.PublicActivityPubIRI, delete.CC)
|
||||
suite.Equal("Delete", delete.Type)
|
||||
|
||||
// wait for the delete to go through
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// the deleted account should be deleted
|
||||
dbAccount, err := suite.db.GetAccountByID(ctx, deletingAccount.ID)
|
||||
suite.NoError(err)
|
||||
suite.WithinDuration(dbAccount.SuspendedAt, time.Now(), 30*time.Second)
|
||||
if !testrig.WaitFor(func() bool {
|
||||
dbAccount, _ := suite.db.GetAccountByID(ctx, deletingAccount.ID)
|
||||
return suite.WithinDuration(dbAccount.SuspendedAt, time.Now(), 30*time.Second)
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for account to be deleted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountTestSuite(t *testing.T) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/stretchr/testify/suite"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type FollowRequestTestSuite struct {
|
||||
|
@ -54,11 +55,22 @@ func (suite *FollowRequestTestSuite) TestFollowRequestAccept() {
|
|||
relationship, errWithCode := suite.processor.FollowRequestAccept(context.Background(), suite.testAutheds["local_account_1"], requestingAccount.ID)
|
||||
suite.NoError(errWithCode)
|
||||
suite.EqualValues(&apimodel.Relationship{ID: "01FHMQX3GAABWSM0S2VZEC2SWC", Following: false, ShowingReblogs: false, Notifying: false, FollowedBy: true, Blocking: false, BlockedBy: false, Muting: false, MutingNotifications: false, Requested: false, DomainBlocking: false, Endorsed: false, Note: ""}, relationship)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// accept should be sent to some_user
|
||||
sent, ok := suite.httpClient.SentMessages[requestingAccount.InboxURI]
|
||||
suite.True(ok)
|
||||
var sent [][]byte
|
||||
if !testrig.WaitFor(func() bool {
|
||||
sentI, ok := suite.httpClient.SentMessages.Load(requestingAccount.InboxURI)
|
||||
if ok {
|
||||
sent, ok = sentI.([][]byte)
|
||||
if !ok {
|
||||
panic("SentMessages entry was not []byte")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for message")
|
||||
}
|
||||
|
||||
accept := &struct {
|
||||
Actor string `json:"actor"`
|
||||
|
@ -73,7 +85,7 @@ func (suite *FollowRequestTestSuite) TestFollowRequestAccept() {
|
|||
To string `json:"to"`
|
||||
Type string `json:"type"`
|
||||
}{}
|
||||
err = json.Unmarshal(sent, accept)
|
||||
err = json.Unmarshal(sent[0], accept)
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(targetAccount.URI, accept.Actor)
|
||||
|
@ -106,11 +118,22 @@ func (suite *FollowRequestTestSuite) TestFollowRequestReject() {
|
|||
relationship, errWithCode := suite.processor.FollowRequestReject(context.Background(), suite.testAutheds["local_account_1"], requestingAccount.ID)
|
||||
suite.NoError(errWithCode)
|
||||
suite.EqualValues(&apimodel.Relationship{ID: "01FHMQX3GAABWSM0S2VZEC2SWC", Following: false, ShowingReblogs: false, Notifying: false, FollowedBy: false, Blocking: false, BlockedBy: false, Muting: false, MutingNotifications: false, Requested: false, DomainBlocking: false, Endorsed: false, Note: ""}, relationship)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// reject should be sent to some_user
|
||||
sent, ok := suite.httpClient.SentMessages[requestingAccount.InboxURI]
|
||||
suite.True(ok)
|
||||
var sent [][]byte
|
||||
if !testrig.WaitFor(func() bool {
|
||||
sentI, ok := suite.httpClient.SentMessages.Load(requestingAccount.InboxURI)
|
||||
if ok {
|
||||
sent, ok = sentI.([][]byte)
|
||||
if !ok {
|
||||
panic("SentMessages entry was not []byte")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for message")
|
||||
}
|
||||
|
||||
reject := &struct {
|
||||
Actor string `json:"actor"`
|
||||
|
@ -125,7 +148,7 @@ func (suite *FollowRequestTestSuite) TestFollowRequestReject() {
|
|||
To string `json:"to"`
|
||||
Type string `json:"type"`
|
||||
}{}
|
||||
err = json.Unmarshal(sent, reject)
|
||||
err = json.Unmarshal(sent[0], reject)
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(targetAccount.URI, reject.Actor)
|
||||
|
|
|
@ -482,6 +482,47 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestUnlocked() {
|
|||
})
|
||||
suite.NoError(err)
|
||||
|
||||
// an accept message should be sent to satan's inbox
|
||||
var sent [][]byte
|
||||
if !testrig.WaitFor(func() bool {
|
||||
sentI, ok := suite.httpClient.SentMessages.Load(originAccount.InboxURI)
|
||||
if ok {
|
||||
sent, ok = sentI.([][]byte)
|
||||
if !ok {
|
||||
panic("SentMessages entry was not []byte")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for message")
|
||||
}
|
||||
|
||||
accept := &struct {
|
||||
Actor string `json:"actor"`
|
||||
ID string `json:"id"`
|
||||
Object struct {
|
||||
Actor string `json:"actor"`
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
To string `json:"to"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
To string `json:"to"`
|
||||
Type string `json:"type"`
|
||||
}{}
|
||||
err = json.Unmarshal(sent[0], accept)
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(targetAccount.URI, accept.Actor)
|
||||
suite.Equal(originAccount.URI, accept.Object.Actor)
|
||||
suite.Equal(satanFollowRequestTurtle.URI, accept.Object.ID)
|
||||
suite.Equal(targetAccount.URI, accept.Object.Object)
|
||||
suite.Equal(targetAccount.URI, accept.Object.To)
|
||||
suite.Equal("Follow", accept.Object.Type)
|
||||
suite.Equal(originAccount.URI, accept.To)
|
||||
suite.Equal("Accept", accept.Type)
|
||||
|
||||
// a notification should be streamed
|
||||
var msg *stream.Message
|
||||
select {
|
||||
|
@ -498,34 +539,6 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestUnlocked() {
|
|||
suite.NoError(err)
|
||||
suite.Equal("follow", notif.Type)
|
||||
suite.Equal(originAccount.ID, notif.Account.ID)
|
||||
|
||||
// an accept message should be sent to satan's inbox
|
||||
suite.Len(suite.httpClient.SentMessages, 1)
|
||||
acceptBytes := suite.httpClient.SentMessages[originAccount.InboxURI]
|
||||
accept := &struct {
|
||||
Actor string `json:"actor"`
|
||||
ID string `json:"id"`
|
||||
Object struct {
|
||||
Actor string `json:"actor"`
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
To string `json:"to"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
To string `json:"to"`
|
||||
Type string `json:"type"`
|
||||
}{}
|
||||
err = json.Unmarshal(acceptBytes, accept)
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(targetAccount.URI, accept.Actor)
|
||||
suite.Equal(originAccount.URI, accept.Object.Actor)
|
||||
suite.Equal(satanFollowRequestTurtle.URI, accept.Object.ID)
|
||||
suite.Equal(targetAccount.URI, accept.Object.Object)
|
||||
suite.Equal(targetAccount.URI, accept.Object.To)
|
||||
suite.Equal("Follow", accept.Object.Type)
|
||||
suite.Equal(originAccount.URI, accept.To)
|
||||
suite.Equal("Accept", accept.Type)
|
||||
}
|
||||
|
||||
// TestCreateStatusFromIRI checks if a forwarded status can be dereferenced by the processor.
|
||||
|
|
|
@ -23,10 +23,10 @@ import (
|
|||
"io"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
@ -99,10 +99,16 @@ func (suite *GetFileTestSuite) TestGetRemoteFileUncached() {
|
|||
suite.Equal(suite.testRemoteAttachments[testAttachment.RemoteURL].Data, b)
|
||||
suite.Equal(suite.testRemoteAttachments[testAttachment.RemoteURL].ContentType, content.ContentType)
|
||||
suite.EqualValues(len(suite.testRemoteAttachments[testAttachment.RemoteURL].Data), content.ContentLength)
|
||||
time.Sleep(2 * time.Second) // wait a few seconds for the media manager to finish doing stuff
|
||||
|
||||
// the attachment should be updated in the database
|
||||
dbAttachment, err := suite.db.GetAttachmentByID(ctx, testAttachment.ID)
|
||||
var dbAttachment *gtsmodel.MediaAttachment
|
||||
if !testrig.WaitFor(func() bool {
|
||||
dbAttachment, err = suite.db.GetAttachmentByID(ctx, testAttachment.ID)
|
||||
return dbAttachment != nil
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for updated attachment")
|
||||
}
|
||||
|
||||
suite.NoError(err)
|
||||
suite.True(*dbAttachment.Cached)
|
||||
|
||||
|
@ -149,12 +155,13 @@ func (suite *GetFileTestSuite) TestGetRemoteFileUncachedInterrupted() {
|
|||
suite.NoError(closer.Close())
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second) // wait a few seconds for the media manager to finish doing stuff
|
||||
|
||||
// the attachment should still be updated in the database even though the caller hung up
|
||||
dbAttachment, err := suite.db.GetAttachmentByID(ctx, testAttachment.ID)
|
||||
suite.NoError(err)
|
||||
suite.True(*dbAttachment.Cached)
|
||||
if !testrig.WaitFor(func() bool {
|
||||
dbAttachment, _ := suite.db.GetAttachmentByID(ctx, testAttachment.ID)
|
||||
return *dbAttachment.Cached
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for attachment to be updated")
|
||||
}
|
||||
|
||||
// the file should be back in storage at the same path as before
|
||||
refreshedBytes, err := suite.storage.Get(ctx, testAttachment.File.Path)
|
||||
|
|
|
@ -33,9 +33,9 @@ type TransTestSuite struct {
|
|||
|
||||
func (suite *TransTestSuite) SetupTest() {
|
||||
testrig.InitTestConfig()
|
||||
testrig.InitTestLog()
|
||||
testrig.InitTestLog()
|
||||
|
||||
suite.testAccounts = testrig.NewTestAccounts()
|
||||
suite.testAccounts = testrig.NewTestAccounts()
|
||||
|
||||
suite.db = testrig.NewTestDB()
|
||||
testrig.StandardDBSetup(suite.db, nil)
|
||||
|
|
|
@ -19,9 +19,6 @@
|
|||
package testrig
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
)
|
||||
|
@ -29,12 +26,11 @@ import (
|
|||
// InitTestConfig initializes viper configuration with test defaults.
|
||||
func InitTestConfig() {
|
||||
config.Config(func(cfg *config.Configuration) {
|
||||
*cfg = TestDefaults
|
||||
*cfg = testDefaults
|
||||
})
|
||||
}
|
||||
|
||||
// TestDefaults returns a Values struct with values set that are suitable for local testing.
|
||||
var TestDefaults = config.Configuration{
|
||||
var testDefaults = config.Configuration{
|
||||
LogLevel: "trace",
|
||||
LogDbQueries: true,
|
||||
ApplicationName: "gotosocial",
|
||||
|
@ -69,8 +65,11 @@ var TestDefaults = config.Configuration{
|
|||
MediaDescriptionMaxChars: 500,
|
||||
MediaRemoteCacheDays: 30,
|
||||
|
||||
StorageBackend: "local",
|
||||
StorageLocalBasePath: path.Join(os.TempDir(), "gotosocial"),
|
||||
// the testrig only uses in-memory storage, so we can
|
||||
// safely set this value to 'test' to avoid running storage
|
||||
// migrations, and other silly things like that
|
||||
StorageBackend: "test",
|
||||
StorageLocalBasePath: "",
|
||||
|
||||
StatusesMaxChars: 5000,
|
||||
StatusesCWMaxChars: 100,
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/superseriousbusiness/activity/pub"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
|
@ -64,7 +65,7 @@ type MockHTTPClient struct {
|
|||
testRemoteServices map[string]vocab.ActivityStreamsService
|
||||
testRemoteAttachments map[string]RemoteAttachmentFile
|
||||
|
||||
SentMessages map[string][]byte
|
||||
SentMessages sync.Map
|
||||
}
|
||||
|
||||
// NewMockHTTPClient returns a client that conforms to the pub.HttpClient interface.
|
||||
|
@ -90,8 +91,6 @@ func NewMockHTTPClient(do func(req *http.Request) (*http.Response, error), relat
|
|||
mockHTTPClient.testRemoteServices = NewTestFediServices()
|
||||
mockHTTPClient.testRemoteAttachments = NewTestFediAttachments(relativeMediaPath)
|
||||
|
||||
mockHTTPClient.SentMessages = make(map[string][]byte)
|
||||
|
||||
mockHTTPClient.do = func(req *http.Request) (*http.Response, error) {
|
||||
responseCode := http.StatusNotFound
|
||||
responseBytes := []byte(`{"error":"404 not found"}`)
|
||||
|
@ -103,7 +102,15 @@ func NewMockHTTPClient(do func(req *http.Request) (*http.Response, error), relat
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mockHTTPClient.SentMessages[req.URL.String()] = b
|
||||
|
||||
if sI, loaded := mockHTTPClient.SentMessages.LoadOrStore(req.URL.String(), [][]byte{b}); loaded {
|
||||
s, ok := sI.([][]byte)
|
||||
if !ok {
|
||||
panic("SentMessages entry wasn't [][]byte")
|
||||
}
|
||||
s = append(s, b)
|
||||
mockHTTPClient.SentMessages.Store(req.URL.String(), s)
|
||||
}
|
||||
|
||||
responseCode = http.StatusOK
|
||||
responseBytes = []byte(`{"ok":"accepted"}`)
|
||||
|
|
|
@ -87,3 +87,28 @@ func TimeMustParse(timeString string) time.Time {
|
|||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// WaitFor calls condition every 200ms, returning true
|
||||
// when condition() returns true, or false after 5s.
|
||||
//
|
||||
// It's useful for when you're waiting for something to
|
||||
// happen, but you don't know exactly how long it will take,
|
||||
// and you want to fail if the thing doesn't happen within 5s.
|
||||
func WaitFor(condition func() bool) bool {
|
||||
tick := time.NewTicker(200 * time.Millisecond)
|
||||
defer tick.Stop()
|
||||
|
||||
timeout := time.NewTimer(5 * time.Second)
|
||||
defer timeout.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
if condition() {
|
||||
return true
|
||||
}
|
||||
case <-timeout.C:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
24
vendor/github.com/stretchr/testify/assert/assertion_compare.go
generated
vendored
24
vendor/github.com/stretchr/testify/assert/assertion_compare.go
generated
vendored
|
@ -1,6 +1,7 @@
|
|||
package assert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
@ -32,7 +33,8 @@ var (
|
|||
|
||||
stringType = reflect.TypeOf("")
|
||||
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
bytesType = reflect.TypeOf([]byte{})
|
||||
)
|
||||
|
||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
|
@ -323,6 +325,26 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
|||
|
||||
return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64)
|
||||
}
|
||||
case reflect.Slice:
|
||||
{
|
||||
// We only care about the []byte type.
|
||||
if !canConvert(obj1Value, bytesType) {
|
||||
break
|
||||
}
|
||||
|
||||
// []byte can be compared!
|
||||
bytesObj1, ok := obj1.([]byte)
|
||||
if !ok {
|
||||
bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte)
|
||||
|
||||
}
|
||||
bytesObj2, ok := obj2.([]byte)
|
||||
if !ok {
|
||||
bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte)
|
||||
}
|
||||
|
||||
return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true
|
||||
}
|
||||
}
|
||||
|
||||
return compareEqual, false
|
||||
|
|
2
vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go
generated
vendored
2
vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go
generated
vendored
|
@ -9,7 +9,7 @@ package assert
|
|||
|
||||
import "reflect"
|
||||
|
||||
// Wrapper around reflect.Value.CanConvert, for compatability
|
||||
// Wrapper around reflect.Value.CanConvert, for compatibility
|
||||
// reasons.
|
||||
func canConvert(value reflect.Value, to reflect.Type) bool {
|
||||
return value.CanConvert(to)
|
||||
|
|
10
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
10
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
|
@ -736,6 +736,16 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim
|
|||
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// YAMLEqf asserts that two YAML strings are equivalent.
|
||||
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
|
20
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
20
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
|
@ -1461,6 +1461,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta
|
|||
return WithinDurationf(a.t, expected, actual, delta, msg, args...)
|
||||
}
|
||||
|
||||
// WithinRange asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return WithinRange(a.t, actual, start, end, msgAndArgs...)
|
||||
}
|
||||
|
||||
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return WithinRangef(a.t, actual, start, end, msg, args...)
|
||||
}
|
||||
|
||||
// YAMLEq asserts that two YAML strings are equivalent.
|
||||
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
|
78
vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
78
vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
|
@ -8,6 +8,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
@ -144,7 +145,8 @@ func CallerInfo() []string {
|
|||
if len(parts) > 1 {
|
||||
dir := parts[len(parts)-2]
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", file, line))
|
||||
path, _ := filepath.Abs(file)
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", path, line))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,16 +565,17 @@ func isEmpty(object interface{}) bool {
|
|||
|
||||
switch objValue.Kind() {
|
||||
// collection types are empty when they have no element
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||
case reflect.Chan, reflect.Map, reflect.Slice:
|
||||
return objValue.Len() == 0
|
||||
// pointers are empty if nil or if the value they point to is empty
|
||||
// pointers are empty if nil or if the value they point to is empty
|
||||
case reflect.Ptr:
|
||||
if objValue.IsNil() {
|
||||
return true
|
||||
}
|
||||
deref := objValue.Elem().Interface()
|
||||
return isEmpty(deref)
|
||||
// for all other types, compare against the zero value
|
||||
// for all other types, compare against the zero value
|
||||
// array types are empty when they match their zero-initialized state
|
||||
default:
|
||||
zero := reflect.Zero(objValue.Type())
|
||||
return reflect.DeepEqual(object, zero.Interface())
|
||||
|
@ -815,7 +818,6 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
|||
return true // we consider nil to be equal to the nil set
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
|
@ -825,14 +827,32 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
|||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice {
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice {
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
listValue := reflect.ValueOf(list)
|
||||
subsetKeys := subsetValue.MapKeys()
|
||||
|
||||
for i := 0; i < len(subsetKeys); i++ {
|
||||
subsetKey := subsetKeys[i]
|
||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
||||
|
||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
|
@ -859,7 +879,6 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
|||
return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
|
@ -869,14 +888,32 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
|||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice {
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice {
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
listValue := reflect.ValueOf(list)
|
||||
subsetKeys := subsetValue.MapKeys()
|
||||
|
||||
for i := 0; i < len(subsetKeys); i++ {
|
||||
subsetKey := subsetKeys[i]
|
||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
||||
|
||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
|
@ -1109,6 +1146,27 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration,
|
|||
return true
|
||||
}
|
||||
|
||||
// WithinRange asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if end.Before(start) {
|
||||
return Fail(t, "Start should be before end", msgAndArgs...)
|
||||
}
|
||||
|
||||
if actual.Before(start) {
|
||||
return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...)
|
||||
} else if actual.After(end) {
|
||||
return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func toFloat(x interface{}) (float64, bool) {
|
||||
var xf float64
|
||||
xok := true
|
||||
|
|
26
vendor/github.com/stretchr/testify/require/require.go
generated
vendored
26
vendor/github.com/stretchr/testify/require/require.go
generated
vendored
|
@ -1864,6 +1864,32 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim
|
|||
t.FailNow()
|
||||
}
|
||||
|
||||
// WithinRange asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.WithinRange(t, actual, start, end, msgAndArgs...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.WithinRangef(t, actual, start, end, msg, args...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// YAMLEq asserts that two YAML strings are equivalent.
|
||||
func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
|
20
vendor/github.com/stretchr/testify/require/require_forward.go
generated
vendored
20
vendor/github.com/stretchr/testify/require/require_forward.go
generated
vendored
|
@ -1462,6 +1462,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta
|
|||
WithinDurationf(a.t, expected, actual, delta, msg, args...)
|
||||
}
|
||||
|
||||
// WithinRange asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
WithinRange(a.t, actual, start, end, msgAndArgs...)
|
||||
}
|
||||
|
||||
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
WithinRangef(a.t, actual, start, end, msg, args...)
|
||||
}
|
||||
|
||||
// YAMLEq asserts that two YAML strings are equivalent.
|
||||
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
|
25
vendor/github.com/stretchr/testify/suite/suite.go
generated
vendored
25
vendor/github.com/stretchr/testify/suite/suite.go
generated
vendored
|
@ -7,6 +7,7 @@ import (
|
|||
"reflect"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -21,17 +22,22 @@ var matchMethod = flag.String("testify.m", "", "regular expression to select tes
|
|||
// retrieving the current *testing.T context.
|
||||
type Suite struct {
|
||||
*assert.Assertions
|
||||
mu sync.RWMutex
|
||||
require *require.Assertions
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
// T retrieves the current *testing.T context.
|
||||
func (suite *Suite) T() *testing.T {
|
||||
suite.mu.RLock()
|
||||
defer suite.mu.RUnlock()
|
||||
return suite.t
|
||||
}
|
||||
|
||||
// SetT sets the current *testing.T context.
|
||||
func (suite *Suite) SetT(t *testing.T) {
|
||||
suite.mu.Lock()
|
||||
defer suite.mu.Unlock()
|
||||
suite.t = t
|
||||
suite.Assertions = assert.New(t)
|
||||
suite.require = require.New(t)
|
||||
|
@ -39,6 +45,8 @@ func (suite *Suite) SetT(t *testing.T) {
|
|||
|
||||
// Require returns a require context for suite.
|
||||
func (suite *Suite) Require() *require.Assertions {
|
||||
suite.mu.Lock()
|
||||
defer suite.mu.Unlock()
|
||||
if suite.require == nil {
|
||||
suite.require = require.New(suite.T())
|
||||
}
|
||||
|
@ -51,14 +59,20 @@ func (suite *Suite) Require() *require.Assertions {
|
|||
// assert.Assertions with require.Assertions), this method is provided so you
|
||||
// can call `suite.Assert().NoError()`.
|
||||
func (suite *Suite) Assert() *assert.Assertions {
|
||||
suite.mu.Lock()
|
||||
defer suite.mu.Unlock()
|
||||
if suite.Assertions == nil {
|
||||
suite.Assertions = assert.New(suite.T())
|
||||
}
|
||||
return suite.Assertions
|
||||
}
|
||||
|
||||
func failOnPanic(t *testing.T) {
|
||||
func recoverAndFailOnPanic(t *testing.T) {
|
||||
r := recover()
|
||||
failOnPanic(t, r)
|
||||
}
|
||||
|
||||
func failOnPanic(t *testing.T, r interface{}) {
|
||||
if r != nil {
|
||||
t.Errorf("test panicked: %v\n%s", r, debug.Stack())
|
||||
t.FailNow()
|
||||
|
@ -81,7 +95,7 @@ func (suite *Suite) Run(name string, subtest func()) bool {
|
|||
// Run takes a testing suite and runs all of the tests attached
|
||||
// to it.
|
||||
func Run(t *testing.T, suite TestingSuite) {
|
||||
defer failOnPanic(t)
|
||||
defer recoverAndFailOnPanic(t)
|
||||
|
||||
suite.SetT(t)
|
||||
|
||||
|
@ -126,10 +140,12 @@ func Run(t *testing.T, suite TestingSuite) {
|
|||
F: func(t *testing.T) {
|
||||
parentT := suite.T()
|
||||
suite.SetT(t)
|
||||
defer failOnPanic(t)
|
||||
defer recoverAndFailOnPanic(t)
|
||||
defer func() {
|
||||
r := recover()
|
||||
|
||||
if stats != nil {
|
||||
passed := !t.Failed()
|
||||
passed := !t.Failed() && r == nil
|
||||
stats.end(method.Name, passed)
|
||||
}
|
||||
|
||||
|
@ -142,6 +158,7 @@ func Run(t *testing.T, suite TestingSuite) {
|
|||
}
|
||||
|
||||
suite.SetT(parentT)
|
||||
failOnPanic(t, r)
|
||||
}()
|
||||
|
||||
if setupTestSuite, ok := suite.(SetupTestSuite); ok {
|
||||
|
|
78
vendor/gopkg.in/yaml.v3/decode.go
generated
vendored
78
vendor/gopkg.in/yaml.v3/decode.go
generated
vendored
|
@ -100,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t {
|
|||
if p.event.typ != yaml_NO_EVENT {
|
||||
return p.event.typ
|
||||
}
|
||||
if !yaml_parser_parse(&p.parser, &p.event) {
|
||||
// It's curious choice from the underlying API to generally return a
|
||||
// positive result on success, but on this case return true in an error
|
||||
// scenario. This was the source of bugs in the past (issue #666).
|
||||
if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR {
|
||||
p.fail()
|
||||
}
|
||||
return p.event.typ
|
||||
|
@ -320,6 +323,8 @@ type decoder struct {
|
|||
decodeCount int
|
||||
aliasCount int
|
||||
aliasDepth int
|
||||
|
||||
mergedFields map[interface{}]bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -808,6 +813,11 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
|
|||
}
|
||||
}
|
||||
|
||||
mergedFields := d.mergedFields
|
||||
d.mergedFields = nil
|
||||
|
||||
var mergeNode *Node
|
||||
|
||||
mapIsNew := false
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.MakeMap(outt))
|
||||
|
@ -815,11 +825,18 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
|
|||
}
|
||||
for i := 0; i < l; i += 2 {
|
||||
if isMerge(n.Content[i]) {
|
||||
d.merge(n.Content[i+1], out)
|
||||
mergeNode = n.Content[i+1]
|
||||
continue
|
||||
}
|
||||
k := reflect.New(kt).Elem()
|
||||
if d.unmarshal(n.Content[i], k) {
|
||||
if mergedFields != nil {
|
||||
ki := k.Interface()
|
||||
if mergedFields[ki] {
|
||||
continue
|
||||
}
|
||||
mergedFields[ki] = true
|
||||
}
|
||||
kkind := k.Kind()
|
||||
if kkind == reflect.Interface {
|
||||
kkind = k.Elem().Kind()
|
||||
|
@ -833,6 +850,12 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.mergedFields = mergedFields
|
||||
if mergeNode != nil {
|
||||
d.merge(n, mergeNode, out)
|
||||
}
|
||||
|
||||
d.stringMapType = stringMapType
|
||||
d.generalMapType = generalMapType
|
||||
return true
|
||||
|
@ -844,7 +867,8 @@ func isStringMap(n *Node) bool {
|
|||
}
|
||||
l := len(n.Content)
|
||||
for i := 0; i < l; i += 2 {
|
||||
if n.Content[i].ShortTag() != strTag {
|
||||
shortTag := n.Content[i].ShortTag()
|
||||
if shortTag != strTag && shortTag != mergeTag {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -861,7 +885,6 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
|
|||
var elemType reflect.Type
|
||||
if sinfo.InlineMap != -1 {
|
||||
inlineMap = out.Field(sinfo.InlineMap)
|
||||
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
|
||||
elemType = inlineMap.Type().Elem()
|
||||
}
|
||||
|
||||
|
@ -870,6 +893,9 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
|
|||
d.prepare(n, field)
|
||||
}
|
||||
|
||||
mergedFields := d.mergedFields
|
||||
d.mergedFields = nil
|
||||
var mergeNode *Node
|
||||
var doneFields []bool
|
||||
if d.uniqueKeys {
|
||||
doneFields = make([]bool, len(sinfo.FieldsList))
|
||||
|
@ -879,13 +905,20 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
|
|||
for i := 0; i < l; i += 2 {
|
||||
ni := n.Content[i]
|
||||
if isMerge(ni) {
|
||||
d.merge(n.Content[i+1], out)
|
||||
mergeNode = n.Content[i+1]
|
||||
continue
|
||||
}
|
||||
if !d.unmarshal(ni, name) {
|
||||
continue
|
||||
}
|
||||
if info, ok := sinfo.FieldsMap[name.String()]; ok {
|
||||
sname := name.String()
|
||||
if mergedFields != nil {
|
||||
if mergedFields[sname] {
|
||||
continue
|
||||
}
|
||||
mergedFields[sname] = true
|
||||
}
|
||||
if info, ok := sinfo.FieldsMap[sname]; ok {
|
||||
if d.uniqueKeys {
|
||||
if doneFields[info.Id] {
|
||||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
|
||||
|
@ -911,6 +944,11 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
|
|||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
d.mergedFields = mergedFields
|
||||
if mergeNode != nil {
|
||||
d.merge(n, mergeNode, out)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -918,19 +956,29 @@ func failWantMap() {
|
|||
failf("map merge requires map or sequence of maps as the value")
|
||||
}
|
||||
|
||||
func (d *decoder) merge(n *Node, out reflect.Value) {
|
||||
switch n.Kind {
|
||||
func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) {
|
||||
mergedFields := d.mergedFields
|
||||
if mergedFields == nil {
|
||||
d.mergedFields = make(map[interface{}]bool)
|
||||
for i := 0; i < len(parent.Content); i += 2 {
|
||||
k := reflect.New(ifaceType).Elem()
|
||||
if d.unmarshal(parent.Content[i], k) {
|
||||
d.mergedFields[k.Interface()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch merge.Kind {
|
||||
case MappingNode:
|
||||
d.unmarshal(n, out)
|
||||
d.unmarshal(merge, out)
|
||||
case AliasNode:
|
||||
if n.Alias != nil && n.Alias.Kind != MappingNode {
|
||||
if merge.Alias != nil && merge.Alias.Kind != MappingNode {
|
||||
failWantMap()
|
||||
}
|
||||
d.unmarshal(n, out)
|
||||
d.unmarshal(merge, out)
|
||||
case SequenceNode:
|
||||
// Step backwards as earlier nodes take precedence.
|
||||
for i := len(n.Content) - 1; i >= 0; i-- {
|
||||
ni := n.Content[i]
|
||||
for i := 0; i < len(merge.Content); i++ {
|
||||
ni := merge.Content[i]
|
||||
if ni.Kind == AliasNode {
|
||||
if ni.Alias != nil && ni.Alias.Kind != MappingNode {
|
||||
failWantMap()
|
||||
|
@ -943,6 +991,8 @@ func (d *decoder) merge(n *Node, out reflect.Value) {
|
|||
default:
|
||||
failWantMap()
|
||||
}
|
||||
|
||||
d.mergedFields = mergedFields
|
||||
}
|
||||
|
||||
func isMerge(n *Node) bool {
|
||||
|
|
11
vendor/gopkg.in/yaml.v3/parserc.go
generated
vendored
11
vendor/gopkg.in/yaml.v3/parserc.go
generated
vendored
|
@ -687,6 +687,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
|
|||
func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
||||
if first {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
parser.marks = append(parser.marks, token.start_mark)
|
||||
skip_token(parser)
|
||||
}
|
||||
|
@ -786,7 +789,7 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
|
|||
}
|
||||
|
||||
token := peek_token(parser)
|
||||
if token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN {
|
||||
if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -813,6 +816,9 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
|
|||
func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
||||
if first {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
parser.marks = append(parser.marks, token.start_mark)
|
||||
skip_token(parser)
|
||||
}
|
||||
|
@ -922,6 +928,9 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev
|
|||
func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
||||
if first {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
parser.marks = append(parser.marks, token.start_mark)
|
||||
skip_token(parser)
|
||||
}
|
||||
|
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
|
@ -346,7 +346,7 @@ github.com/spf13/viper/internal/encoding/javaproperties
|
|||
github.com/spf13/viper/internal/encoding/json
|
||||
github.com/spf13/viper/internal/encoding/toml
|
||||
github.com/spf13/viper/internal/encoding/yaml
|
||||
# github.com/stretchr/testify v1.7.1
|
||||
# github.com/stretchr/testify v1.8.0
|
||||
## explicit; go 1.13
|
||||
github.com/stretchr/testify/assert
|
||||
github.com/stretchr/testify/require
|
||||
|
@ -750,7 +750,7 @@ gopkg.in/square/go-jose.v2/json
|
|||
# gopkg.in/yaml.v2 v2.4.0
|
||||
## explicit; go 1.15
|
||||
gopkg.in/yaml.v2
|
||||
# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
# gopkg.in/yaml.v3 v3.0.1
|
||||
## explicit
|
||||
gopkg.in/yaml.v3
|
||||
# lukechampine.com/uint128 v1.2.0
|
||||
|
|
Loading…
Reference in a new issue