mirror of
https://github.com/elyby/chrly.git
synced 2024-11-30 10:42:14 +05:30
288 lines
8.5 KiB
Go
288 lines
8.5 KiB
Go
package mojangtextures
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"strings"
|
|
"testing"
|
|
|
|
testify "github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"github.com/elyby/chrly/api/mojang"
|
|
mocks "github.com/elyby/chrly/tests"
|
|
)
|
|
|
|
func TestJobsQueue(t *testing.T) {
|
|
createQueue := func() *jobsQueue {
|
|
queue := &jobsQueue{}
|
|
queue.New()
|
|
|
|
return queue
|
|
}
|
|
|
|
t.Run("Enqueue", func(t *testing.T) {
|
|
assert := testify.New(t)
|
|
|
|
s := createQueue()
|
|
s.Enqueue(&jobItem{username: "username1"})
|
|
s.Enqueue(&jobItem{username: "username2"})
|
|
s.Enqueue(&jobItem{username: "username3"})
|
|
|
|
assert.Equal(3, s.Size())
|
|
})
|
|
|
|
t.Run("Dequeue", func(t *testing.T) {
|
|
assert := testify.New(t)
|
|
|
|
s := createQueue()
|
|
s.Enqueue(&jobItem{username: "username1"})
|
|
s.Enqueue(&jobItem{username: "username2"})
|
|
s.Enqueue(&jobItem{username: "username3"})
|
|
s.Enqueue(&jobItem{username: "username4"})
|
|
|
|
items := s.Dequeue(2)
|
|
assert.Len(items, 2)
|
|
assert.Equal("username1", items[0].username)
|
|
assert.Equal("username2", items[1].username)
|
|
assert.Equal(2, s.Size())
|
|
|
|
items = s.Dequeue(40)
|
|
assert.Len(items, 2)
|
|
assert.Equal("username3", items[0].username)
|
|
assert.Equal("username4", items[1].username)
|
|
})
|
|
}
|
|
|
|
// This is really stupid test just to get 100% coverage on this package :)
|
|
func TestBatchUuidsProvider_forever(t *testing.T) {
|
|
testify.True(t, forever())
|
|
}
|
|
|
|
type mojangUsernamesToUuidsRequestMock struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (o *mojangUsernamesToUuidsRequestMock) UsernamesToUuids(usernames []string) ([]*mojang.ProfileInfo, error) {
|
|
args := o.Called(usernames)
|
|
var result []*mojang.ProfileInfo
|
|
if casted, ok := args.Get(0).([]*mojang.ProfileInfo); ok {
|
|
result = casted
|
|
}
|
|
|
|
return result, args.Error(1)
|
|
}
|
|
|
|
type batchUuidsProviderGetUuidResult struct {
|
|
Result *mojang.ProfileInfo
|
|
Error error
|
|
}
|
|
|
|
type batchUuidsProviderTestSuite struct {
|
|
suite.Suite
|
|
|
|
Provider *BatchUuidsProvider
|
|
GetUuidAsync func(username string) chan *batchUuidsProviderGetUuidResult
|
|
|
|
Logger *mocks.WdMock
|
|
MojangApi *mojangUsernamesToUuidsRequestMock
|
|
|
|
Iterate func()
|
|
done func()
|
|
iterateChan chan bool
|
|
}
|
|
|
|
func (suite *batchUuidsProviderTestSuite) SetupTest() {
|
|
suite.Logger = &mocks.WdMock{}
|
|
|
|
suite.Provider = &BatchUuidsProvider{
|
|
Logger: suite.Logger,
|
|
IterationDelay: 0,
|
|
IterationSize: 10,
|
|
}
|
|
|
|
suite.iterateChan = make(chan bool)
|
|
forever = func() bool {
|
|
return <-suite.iterateChan
|
|
}
|
|
|
|
suite.Iterate = func() {
|
|
suite.iterateChan <- true
|
|
}
|
|
|
|
suite.done = func() {
|
|
suite.iterateChan <- false
|
|
}
|
|
|
|
suite.GetUuidAsync = func(username string) chan *batchUuidsProviderGetUuidResult {
|
|
s := make(chan bool)
|
|
// This dirty hack ensures, that the username will be queued before we return control to the caller.
|
|
// It's needed to keep expected calls order and prevent cases when iteration happens before all usernames
|
|
// will be queued.
|
|
suite.Logger.On("IncCounter", "mojang_textures.usernames.queued", int64(1)).Once().Run(func(args mock.Arguments) {
|
|
s <- true
|
|
})
|
|
|
|
c := make(chan *batchUuidsProviderGetUuidResult)
|
|
go func() {
|
|
profile, err := suite.Provider.GetUuid(username)
|
|
c <- &batchUuidsProviderGetUuidResult{
|
|
Result: profile,
|
|
Error: err,
|
|
}
|
|
}()
|
|
|
|
<-s
|
|
|
|
return c
|
|
}
|
|
|
|
suite.MojangApi = &mojangUsernamesToUuidsRequestMock{}
|
|
usernamesToUuids = suite.MojangApi.UsernamesToUuids
|
|
}
|
|
|
|
func (suite *batchUuidsProviderTestSuite) TearDownTest() {
|
|
suite.done()
|
|
suite.MojangApi.AssertExpectations(suite.T())
|
|
suite.Logger.AssertExpectations(suite.T())
|
|
}
|
|
|
|
func TestBatchUuidsProvider(t *testing.T) {
|
|
suite.Run(t, new(batchUuidsProviderTestSuite))
|
|
}
|
|
|
|
func (suite *batchUuidsProviderTestSuite) TestGetUuidForOneUsername() {
|
|
expectedResult := &mojang.ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
|
|
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.iteration_size", int64(1)).Once()
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.queue_size", int64(0)).Once()
|
|
suite.Logger.On("RecordTimer", "mojang_textures.usernames.round_time", mock.Anything).Once()
|
|
|
|
suite.MojangApi.On("UsernamesToUuids", []string{"username"}).Once().Return([]*mojang.ProfileInfo{expectedResult}, nil)
|
|
|
|
resultChan := suite.GetUuidAsync("username")
|
|
|
|
suite.Iterate()
|
|
|
|
result := <-resultChan
|
|
suite.Assert().Equal(expectedResult, result.Result)
|
|
suite.Assert().Nil(result.Error)
|
|
}
|
|
|
|
func (suite *batchUuidsProviderTestSuite) TestGetUuidForTwoUsernames() {
|
|
expectedResult1 := &mojang.ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username1"}
|
|
expectedResult2 := &mojang.ProfileInfo{Id: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Name: "username2"}
|
|
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.iteration_size", int64(2)).Once()
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.queue_size", int64(0)).Once()
|
|
suite.Logger.On("RecordTimer", "mojang_textures.usernames.round_time", mock.Anything).Once()
|
|
|
|
suite.MojangApi.On("UsernamesToUuids", []string{"username1", "username2"}).Once().Return([]*mojang.ProfileInfo{
|
|
expectedResult1,
|
|
expectedResult2,
|
|
}, nil)
|
|
|
|
resultChan1 := suite.GetUuidAsync("username1")
|
|
resultChan2 := suite.GetUuidAsync("username2")
|
|
|
|
suite.Iterate()
|
|
|
|
result1 := <-resultChan1
|
|
suite.Assert().Equal(expectedResult1, result1.Result)
|
|
suite.Assert().Nil(result1.Error)
|
|
|
|
result2 := <-resultChan2
|
|
suite.Assert().Equal(expectedResult2, result2.Result)
|
|
suite.Assert().Nil(result2.Error)
|
|
}
|
|
|
|
func (suite *batchUuidsProviderTestSuite) TestGetUuidForMoreThan10Usernames() {
|
|
usernames := make([]string, 12)
|
|
for i := 0; i < cap(usernames); i++ {
|
|
usernames[i] = randStr(8)
|
|
}
|
|
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.iteration_size", int64(10)).Once()
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.iteration_size", int64(2)).Once()
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.queue_size", int64(2)).Once()
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.queue_size", int64(0)).Once()
|
|
suite.Logger.On("RecordTimer", "mojang_textures.usernames.round_time", mock.Anything).Twice()
|
|
|
|
suite.MojangApi.On("UsernamesToUuids", mock.MatchedBy(func(usernames []string) bool {
|
|
return len(usernames) == 10
|
|
})).Once().Return([]*mojang.ProfileInfo{}, nil)
|
|
suite.MojangApi.On("UsernamesToUuids", mock.MatchedBy(func(usernames []string) bool {
|
|
return len(usernames) == 2
|
|
})).Once().Return([]*mojang.ProfileInfo{}, nil)
|
|
|
|
channels := make([]chan *batchUuidsProviderGetUuidResult, len(usernames))
|
|
for i, username := range usernames {
|
|
channels[i] = suite.GetUuidAsync(username)
|
|
}
|
|
|
|
suite.Iterate()
|
|
suite.Iterate()
|
|
|
|
for _, channel := range channels {
|
|
<-channel
|
|
}
|
|
}
|
|
|
|
func (suite *batchUuidsProviderTestSuite) TestDoNothingWhenNoTasks() {
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.iteration_size", int64(1)).Once()
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.iteration_size", int64(0)).Twice()
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.queue_size", int64(0)).Times(3)
|
|
suite.Logger.On("RecordTimer", "mojang_textures.usernames.round_time", mock.Anything)
|
|
|
|
suite.MojangApi.On("UsernamesToUuids", []string{"username"}).Once().Return([]*mojang.ProfileInfo{}, nil)
|
|
|
|
// Perform first iteration and await it finishes
|
|
resultChan := suite.GetUuidAsync("username")
|
|
|
|
suite.Iterate()
|
|
|
|
result := <-resultChan
|
|
suite.Assert().Nil(result.Result)
|
|
suite.Assert().Nil(result.Error)
|
|
|
|
// Let it to perform a few more iterations to ensure, that there are no calls to external APIs
|
|
suite.Iterate()
|
|
suite.Iterate()
|
|
}
|
|
|
|
func (suite *batchUuidsProviderTestSuite) TestGetUuidForTwoUsernamesWithAnError() {
|
|
expectedError := &mojang.TooManyRequestsError{}
|
|
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.iteration_size", int64(2)).Once()
|
|
suite.Logger.On("UpdateGauge", "mojang_textures.usernames.queue_size", int64(0)).Once()
|
|
suite.Logger.On("RecordTimer", "mojang_textures.usernames.round_time", mock.Anything).Once()
|
|
|
|
suite.MojangApi.On("UsernamesToUuids", []string{"username1", "username2"}).Once().Return(nil, expectedError)
|
|
|
|
resultChan1 := suite.GetUuidAsync("username1")
|
|
resultChan2 := suite.GetUuidAsync("username2")
|
|
|
|
suite.Iterate()
|
|
|
|
result1 := <-resultChan1
|
|
suite.Assert().Nil(result1.Result)
|
|
suite.Assert().Equal(expectedError, result1.Error)
|
|
|
|
result2 := <-resultChan2
|
|
suite.Assert().Nil(result2.Result)
|
|
suite.Assert().Equal(expectedError, result2.Error)
|
|
}
|
|
|
|
var replacer = strings.NewReplacer("-", "_", "=", "")
|
|
|
|
// https://stackoverflow.com/a/50581165
|
|
func randStr(len int) string {
|
|
buff := make([]byte, len)
|
|
_, _ = rand.Read(buff)
|
|
str := replacer.Replace(base64.URLEncoding.EncodeToString(buff))
|
|
|
|
// Base 64 can be longer than len
|
|
return str[:len]
|
|
}
|