chrly/internal/mojang/batch_uuids_provider_test.go

211 lines
5.9 KiB
Go

package mojang
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
)
var awaitDelay = 20 * time.Millisecond
type mojangUsernamesToUuidsRequestMock struct {
mock.Mock
}
func (o *mojangUsernamesToUuidsRequestMock) UsernamesToUuids(ctx context.Context, usernames []string) ([]*ProfileInfo, error) {
args := o.Called(ctx, usernames)
var result []*ProfileInfo
if casted, ok := args.Get(0).([]*ProfileInfo); ok {
result = casted
}
return result, args.Error(1)
}
type batchUuidsProviderGetUuidResult struct {
Result *ProfileInfo
Error error
}
type batchUuidsProviderTestSuite struct {
suite.Suite
Provider *BatchUuidsProvider
MojangApi *mojangUsernamesToUuidsRequestMock
}
func (s *batchUuidsProviderTestSuite) SetupTest() {
s.MojangApi = &mojangUsernamesToUuidsRequestMock{}
s.Provider = NewBatchUuidsProvider(
s.MojangApi.UsernamesToUuids,
3,
awaitDelay,
false,
)
}
func (s *batchUuidsProviderTestSuite) TearDownTest() {
s.MojangApi.AssertExpectations(s.T())
s.Provider.StopQueue()
}
func (s *batchUuidsProviderTestSuite) GetUuidAsync(username string) <-chan *batchUuidsProviderGetUuidResult {
return s.GetUuidAsyncWithCtx(context.Background(), username)
}
func (s *batchUuidsProviderTestSuite) GetUuidAsyncWithCtx(ctx context.Context, username string) <-chan *batchUuidsProviderGetUuidResult {
startedChan := make(chan any)
c := make(chan *batchUuidsProviderGetUuidResult, 1)
go func() {
close(startedChan)
profile, err := s.Provider.GetUuid(ctx, username)
c <- &batchUuidsProviderGetUuidResult{
Result: profile,
Error: err,
}
close(c)
}()
<-startedChan
return c
}
func (s *batchUuidsProviderTestSuite) TestGetUuidForFewUsernamesSuccessfully() {
expectedUsernames := []string{"username1", "username2"}
expectedResult1 := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username1"}
expectedResult2 := &ProfileInfo{Id: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Name: "username2"}
s.MojangApi.On("UsernamesToUuids", mock.Anything, expectedUsernames).Once().Return([]*ProfileInfo{
expectedResult1,
expectedResult2,
}, nil)
chan1 := s.GetUuidAsync("username1")
chan2 := s.GetUuidAsync("username2")
s.Require().Empty(chan1)
s.Require().Empty(chan2)
time.Sleep(time.Duration(float64(awaitDelay) * 1.5))
result1 := <-chan1
result2 := <-chan2
s.Require().NoError(result1.Error)
s.Require().Equal(expectedResult1, result1.Result)
s.Require().NoError(result2.Error)
s.Require().Equal(expectedResult2, result2.Result)
// Await a few more iterations to ensure, that no requests will be performed when there are no additional tasks
time.Sleep(awaitDelay * 3)
}
func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesSplitByMultipleIterations() {
var emptyResponse []string
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username1", "username2", "username3"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username4"}).Once().Return(emptyResponse, nil)
resultChan1 := s.GetUuidAsync("username1")
resultChan2 := s.GetUuidAsync("username2")
resultChan3 := s.GetUuidAsync("username3")
resultChan4 := s.GetUuidAsync("username4")
time.Sleep(time.Duration(float64(awaitDelay) * 1.5))
s.Require().NotEmpty(resultChan1)
s.Require().NotEmpty(resultChan2)
s.Require().NotEmpty(resultChan3)
s.Require().Empty(resultChan4)
time.Sleep(time.Duration(float64(awaitDelay) * 1.5))
s.Require().NotEmpty(resultChan4)
}
func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesWhenOneOfContextIsDeadlined() {
var emptyResponse []string
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username1", "username2", "username4"}).Once().Return(emptyResponse, nil)
ctx, cancelCtx := context.WithCancel(context.Background())
resultChan1 := s.GetUuidAsync("username1")
resultChan2 := s.GetUuidAsync("username2")
resultChan3 := s.GetUuidAsyncWithCtx(ctx, "username3")
resultChan4 := s.GetUuidAsync("username4")
cancelCtx()
time.Sleep(time.Duration(float64(awaitDelay) * 0.5))
s.Empty(resultChan1)
s.Empty(resultChan2)
s.NotEmpty(resultChan3, "canceled context must immediately release the job")
s.Empty(resultChan4)
result3 := <-resultChan3
s.Nil(result3.Result)
s.ErrorIs(result3.Error, context.Canceled)
time.Sleep(awaitDelay)
s.Require().NotEmpty(resultChan1)
s.Require().NotEmpty(resultChan2)
s.Require().NotEmpty(resultChan4)
}
func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesFireOnFull() {
s.Provider.fireOnFull = true
var emptyResponse []string
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username1", "username2", "username3"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username4"}).Once().Return(emptyResponse, nil)
resultChan1 := s.GetUuidAsync("username1")
resultChan2 := s.GetUuidAsync("username2")
resultChan3 := s.GetUuidAsync("username3")
resultChan4 := s.GetUuidAsync("username4")
time.Sleep(time.Duration(float64(awaitDelay) * 0.5))
s.Require().NotEmpty(resultChan1)
s.Require().NotEmpty(resultChan2)
s.Require().NotEmpty(resultChan3)
s.Require().Empty(resultChan4)
time.Sleep(time.Duration(float64(awaitDelay) * 1.5))
s.Require().NotEmpty(resultChan4)
}
func (s *batchUuidsProviderTestSuite) TestGetUuidForFewUsernamesWithAnError() {
expectedUsernames := []string{"username1", "username2"}
expectedError := errors.New("mock error")
s.MojangApi.On("UsernamesToUuids", mock.Anything, expectedUsernames).Once().Return(nil, expectedError)
resultChan1 := s.GetUuidAsync("username1")
resultChan2 := s.GetUuidAsync("username2")
result1 := <-resultChan1
s.Assert().Nil(result1.Result)
s.Assert().Equal(expectedError, result1.Error)
result2 := <-resultChan2
s.Assert().Nil(result2.Result)
s.Assert().Equal(expectedError, result2.Error)
}
func TestBatchUuidsProvider(t *testing.T) {
suite.Run(t, new(batchUuidsProviderTestSuite))
}