mirror of
				https://github.com/elyby/chrly.git
				synced 2025-05-31 14:11:51 +05:30 
			
		
		
		
	
		
			
				
	
	
		
			301 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			9.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"
 | 
						|
)
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
	Emitter   *mockEmitter
 | 
						|
	MojangApi *mojangUsernamesToUuidsRequestMock
 | 
						|
 | 
						|
	Iterate     func()
 | 
						|
	done        func()
 | 
						|
	iterateChan chan bool
 | 
						|
}
 | 
						|
 | 
						|
func (suite *batchUuidsProviderTestSuite) SetupTest() {
 | 
						|
	suite.Emitter = &mockEmitter{}
 | 
						|
 | 
						|
	suite.Provider = &BatchUuidsProvider{
 | 
						|
		Emitter:        suite.Emitter,
 | 
						|
		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.Emitter.On("Emit",
 | 
						|
			"mojang_textures:batch_uuids_provider:queued",
 | 
						|
			username,
 | 
						|
		).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.Emitter.AssertExpectations(suite.T())
 | 
						|
	suite.MojangApi.AssertExpectations(suite.T())
 | 
						|
}
 | 
						|
 | 
						|
func TestBatchUuidsProvider(t *testing.T) {
 | 
						|
	suite.Run(t, new(batchUuidsProviderTestSuite))
 | 
						|
}
 | 
						|
 | 
						|
func (suite *batchUuidsProviderTestSuite) TestGetUuidForOneUsername() {
 | 
						|
	expectedUsernames := []string{"username"}
 | 
						|
	expectedResult := &mojang.ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
 | 
						|
	expectedResponse := []*mojang.ProfileInfo{expectedResult}
 | 
						|
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:before_round").Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", expectedUsernames, 0).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:result", expectedUsernames, expectedResponse, nil).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").Once()
 | 
						|
 | 
						|
	suite.MojangApi.On("UsernamesToUuids", expectedUsernames).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() {
 | 
						|
	expectedUsernames := []string{"username1", "username2"}
 | 
						|
	expectedResult1 := &mojang.ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username1"}
 | 
						|
	expectedResult2 := &mojang.ProfileInfo{Id: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Name: "username2"}
 | 
						|
	expectedResponse := []*mojang.ProfileInfo{expectedResult1, expectedResult2}
 | 
						|
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:before_round").Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", expectedUsernames, 0).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:result", expectedUsernames, expectedResponse, nil).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").Once()
 | 
						|
 | 
						|
	suite.MojangApi.On("UsernamesToUuids", expectedUsernames).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)
 | 
						|
	}
 | 
						|
 | 
						|
	// In this test we're not testing response, so always return an empty resultset
 | 
						|
	expectedResponse := []*mojang.ProfileInfo{}
 | 
						|
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:before_round").Twice()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", usernames[0:10], 2).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:result", usernames[0:10], expectedResponse, nil).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", usernames[10:12], 0).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:result", usernames[10:12], expectedResponse, nil).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").Twice()
 | 
						|
 | 
						|
	suite.MojangApi.On("UsernamesToUuids", usernames[0:10]).Once().Return(expectedResponse, nil)
 | 
						|
	suite.MojangApi.On("UsernamesToUuids", usernames[10:12]).Once().Return(expectedResponse, 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.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:before_round").Times(3)
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", []string{"username"}, 0).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:result", []string{"username"}, mock.Anything, nil).Once()
 | 
						|
	var nilStringSlice []string
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", nilStringSlice, 0).Twice()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").Times(3)
 | 
						|
 | 
						|
	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() {
 | 
						|
	expectedUsernames := []string{"username1", "username2"}
 | 
						|
	expectedError := &mojang.TooManyRequestsError{}
 | 
						|
	var nilProfilesResponse []*mojang.ProfileInfo
 | 
						|
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:before_round").Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", expectedUsernames, 0).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:result", expectedUsernames, nilProfilesResponse, expectedError).Once()
 | 
						|
	suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").Once()
 | 
						|
 | 
						|
	suite.MojangApi.On("UsernamesToUuids", expectedUsernames).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]
 | 
						|
}
 |