#1: Add storage integration

This commit is contained in:
ErickSkrauch 2019-04-20 19:35:37 +03:00
parent abea94a41f
commit b1e18d0d01
3 changed files with 81 additions and 53 deletions

View File

@ -39,9 +39,9 @@ func TestDequeueN(t *testing.T) {
assert.True(s.IsEmpty())
}
func createQueue() jobsQueue {
s := jobsQueue{}
s.New()
func createQueue() *jobsQueue {
queue := &jobsQueue{}
queue.New()
return s
return queue
}

View File

@ -31,6 +31,17 @@ func (ctx *JobsQueue) GetTexturesForUsername(username string) chan *mojang.Signe
})
responseChan := make(chan *mojang.SignedTexturesResponse)
cachedResult := ctx.Storage.Get(username)
if cachedResult != nil {
go func() {
responseChan <- cachedResult
close(responseChan)
}()
return responseChan
}
isFirstListener := ctx.broadcast.AddListener(username, responseChan)
if isFirstListener {
resultChan := make(chan *mojang.SignedTexturesResponse)
@ -39,6 +50,7 @@ func (ctx *JobsQueue) GetTexturesForUsername(username string) chan *mojang.Signe
go func() {
result := <-resultChan
close(resultChan)
ctx.broadcast.BroadcastAndRemove(username, result)
}()
}
@ -108,11 +120,11 @@ func (ctx *JobsQueue) queueRound() {
wg.Done()
job.RespondTo <- result
if shouldCache {
// TODO: store result to cache
if shouldCache && result != nil {
ctx.Storage.Set(result)
}
job.RespondTo <- result
}(job)
}

View File

@ -36,9 +36,28 @@ func (o *MojangApiMocks) UuidToTextures(uuid string, signed bool) (*mojang.Signe
return result, args.Error(1)
}
type MockStorage struct {
mock.Mock
}
func (m *MockStorage) Get(username string) *mojang.SignedTexturesResponse {
args := m.Called(username)
var result *mojang.SignedTexturesResponse
if casted, ok := args.Get(0).(*mojang.SignedTexturesResponse); ok {
result = casted
}
return result
}
func (m *MockStorage) Set(textures *mojang.SignedTexturesResponse) {
m.Called(textures)
}
type QueueTestSuite struct {
suite.Suite
Queue *JobsQueue
Storage *MockStorage
MojangApi *MojangApiMocks
Iterate func()
@ -51,7 +70,9 @@ func (suite *QueueTestSuite) SetupSuite() {
}
func (suite *QueueTestSuite) SetupTest() {
suite.Queue = &JobsQueue{}
suite.Storage = &MockStorage{}
suite.Queue = &JobsQueue{Storage: suite.Storage}
suite.iterateChan = make(chan bool)
forever = func() bool {
@ -74,49 +95,48 @@ func (suite *QueueTestSuite) SetupTest() {
func (suite *QueueTestSuite) TearDownTest() {
suite.done()
suite.MojangApi.AssertExpectations(suite.T())
suite.Storage.AssertExpectations(suite.T())
}
func (suite *QueueTestSuite) TestReceiveTexturesForOneUsername() {
expectedResult := &mojang.SignedTexturesResponse{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"}
suite.Storage.On("Get", mock.Anything).Return(nil)
suite.Storage.On("Set", expectedResult).Once()
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
}, nil)
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(
&mojang.SignedTexturesResponse{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
nil,
)
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(expectedResult, nil)
resultChan := suite.Queue.GetTexturesForUsername("maksimkurb")
suite.Iterate()
result := <-resultChan
if suite.Assert().NotNil(result) {
suite.Assert().Equal("0d252b7218b648bfb86c2ae476954d32", result.Id)
suite.Assert().Equal("maksimkurb", result.Name)
}
suite.Assert().Equal(expectedResult, result)
}
func (suite *QueueTestSuite) TestReceiveTexturesForFewUsernames() {
expectedResult1 := &mojang.SignedTexturesResponse{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"}
expectedResult2 := &mojang.SignedTexturesResponse{Id: "4566e69fc90748ee8d71d7ba5aa00d20", Name: "Thinkofdeath"}
suite.Storage.On("Get", mock.Anything).Return(nil)
suite.Storage.On("Set", expectedResult1).Once()
suite.Storage.On("Set", expectedResult2).Once()
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb", "Thinkofdeath"}).Once().Return([]*mojang.ProfileInfo{
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
{Id: "4566e69fc90748ee8d71d7ba5aa00d20", Name: "Thinkofdeath"},
}, nil)
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(
&mojang.SignedTexturesResponse{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
nil,
)
suite.MojangApi.On("UuidToTextures", "4566e69fc90748ee8d71d7ba5aa00d20", true).Once().Return(
&mojang.SignedTexturesResponse{Id: "4566e69fc90748ee8d71d7ba5aa00d20", Name: "Thinkofdeath"},
nil,
)
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(expectedResult1, nil)
suite.MojangApi.On("UuidToTextures", "4566e69fc90748ee8d71d7ba5aa00d20", true).Once().Return(expectedResult2, nil)
resultChan1 := suite.Queue.GetTexturesForUsername("maksimkurb")
resultChan2 := suite.Queue.GetTexturesForUsername("Thinkofdeath")
suite.Iterate()
suite.Assert().NotNil(<-resultChan1)
suite.Assert().NotNil(<-resultChan2)
suite.Assert().Equal(expectedResult1, <-resultChan1)
suite.Assert().Equal(expectedResult2, <-resultChan2)
}
func (suite *QueueTestSuite) TestReceiveTexturesForMoreThan100Usernames() {
@ -125,6 +145,8 @@ func (suite *QueueTestSuite) TestReceiveTexturesForMoreThan100Usernames() {
usernames[i] = randStr(8)
}
suite.Storage.On("Get", mock.Anything).Times(120).Return(nil)
// Storage.Set shouldn't be called
suite.MojangApi.On("UsernameToUuids", usernames[0:100]).Once().Return([]*mojang.ProfileInfo{}, nil)
suite.MojangApi.On("UsernameToUuids", usernames[100:120]).Once().Return([]*mojang.ProfileInfo{}, nil)
@ -137,41 +159,36 @@ func (suite *QueueTestSuite) TestReceiveTexturesForMoreThan100Usernames() {
}
func (suite *QueueTestSuite) TestReceiveTexturesForTheSameUsernames() {
expectedResult := &mojang.SignedTexturesResponse{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"}
suite.Storage.On("Get", mock.Anything).Twice().Return(nil)
suite.Storage.On("Set", expectedResult).Once()
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
}, nil)
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(
&mojang.SignedTexturesResponse{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
nil,
)
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(expectedResult, nil)
resultChan1 := suite.Queue.GetTexturesForUsername("maksimkurb")
resultChan2 := suite.Queue.GetTexturesForUsername("maksimkurb")
suite.Iterate()
result1 := <-resultChan1
result2 := <-resultChan2
if suite.Assert().NotNil(result1) {
suite.Assert().Equal("0d252b7218b648bfb86c2ae476954d32", result1.Id)
suite.Assert().Equal("maksimkurb", result1.Name)
suite.Assert().Equal(result1, result2)
}
suite.Assert().Equal(expectedResult, <-resultChan1)
suite.Assert().Equal(expectedResult, <-resultChan2)
}
func (suite *QueueTestSuite) TestReceiveTexturesForUsernameThatAlreadyProcessing() {
expectedResult := &mojang.SignedTexturesResponse{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"}
suite.Storage.On("Get", mock.Anything).Return(nil)
suite.Storage.On("Set", expectedResult).Once()
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
}, nil)
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).
Once().
After(10*time.Millisecond). // Simulate long round trip
Return(
&mojang.SignedTexturesResponse{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
nil,
)
Return(expectedResult, nil)
resultChan1 := suite.Queue.GetTexturesForUsername("maksimkurb")
@ -183,18 +200,13 @@ func (suite *QueueTestSuite) TestReceiveTexturesForUsernameThatAlreadyProcessing
resultChan2 := suite.Queue.GetTexturesForUsername("maksimkurb")
result1 := <-resultChan1
result2 := <-resultChan2
if suite.Assert().NotNil(result1) {
suite.Assert().Equal("0d252b7218b648bfb86c2ae476954d32", result1.Id)
suite.Assert().Equal("maksimkurb", result1.Name)
suite.Assert().Equal(result1, result2)
}
suite.Assert().Equal(expectedResult, <-resultChan1)
suite.Assert().Equal(expectedResult, <-resultChan2)
}
func (suite *QueueTestSuite) TestDoNothingWhenNoTasks() {
suite.Storage.On("Get", mock.Anything).Return(nil)
// Storage.Set shouldn't be called
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{}, nil)
// Perform first iteration and await it finish
@ -210,6 +222,8 @@ func (suite *QueueTestSuite) TestDoNothingWhenNoTasks() {
}
func (suite *QueueTestSuite) TestHandle429ResponseWhenExchangingUsernamesToUuids() {
suite.Storage.On("Get", mock.Anything).Return(nil)
// Storage.Set shouldn't be called
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return(nil, &mojang.TooManyRequestsError{})
resultChan := suite.Queue.GetTexturesForUsername("maksimkurb")
@ -220,6 +234,8 @@ func (suite *QueueTestSuite) TestHandle429ResponseWhenExchangingUsernamesToUuids
}
func (suite *QueueTestSuite) TestHandle429ResponseWhenRequestingUsersTextures() {
suite.Storage.On("Get", mock.Anything).Return(nil)
// Storage.Set shouldn't be called
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
}, nil)