mirror of
https://github.com/elyby/chrly.git
synced 2024-12-23 13:40:11 +05:30
#1: add initial tests for queue, upgrade github.com/stretchr/testify
This commit is contained in:
parent
fd4e5eb9ca
commit
e14619e079
6
Gopkg.lock
generated
6
Gopkg.lock
generated
@ -227,12 +227,12 @@
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3926a4ec9a4ff1a072458451aa2d9b98acd059a45b38f7335d31e06c3d6a0159"
|
||||
digest = "1:381bcbeb112a51493d9d998bbba207a529c73dbb49b3fd789e48c63fac1f192c"
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
pruneopts = ""
|
||||
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
||||
version = "v1.1.4"
|
||||
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "issue-18"
|
||||
|
@ -33,7 +33,7 @@ ignored = ["github.com/elyby/chrly"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "^1.1.4"
|
||||
version = "^1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang/mock"
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
var usernamesToUuids = mojang.UsernamesToUuids
|
||||
var uuidToTextures = mojang.UuidToTextures
|
||||
var delay = time.Second
|
||||
|
||||
type JobsQueue struct {
|
||||
Storage Storage
|
||||
@ -18,24 +19,26 @@ type JobsQueue struct {
|
||||
queue jobsQueue
|
||||
}
|
||||
|
||||
func (ctx *JobsQueue) GetTexturesForUsername(username string) (resultChan chan *mojang.SignedTexturesResponse) {
|
||||
func (ctx *JobsQueue) GetTexturesForUsername(username string) *mojang.SignedTexturesResponse {
|
||||
ctx.onFirstCall.Do(func() {
|
||||
ctx.queue.New()
|
||||
ctx.startQueue()
|
||||
})
|
||||
|
||||
resultChan := make(chan *mojang.SignedTexturesResponse)
|
||||
// TODO: prevent of adding the same username more than once
|
||||
ctx.queue.Enqueue(&jobItem{username, resultChan})
|
||||
|
||||
return
|
||||
return <-resultChan
|
||||
}
|
||||
|
||||
func (ctx *JobsQueue) startQueue() {
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(delay)
|
||||
for true {
|
||||
start := time.Now()
|
||||
ctx.queueRound()
|
||||
time.Sleep(time.Second - time.Since(start))
|
||||
time.Sleep(delay - time.Since(start))
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -66,7 +69,7 @@ func (ctx *JobsQueue) queueRound() {
|
||||
var wg sync.WaitGroup
|
||||
for _, job := range jobs {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
go func(job *jobItem) {
|
||||
var result *mojang.SignedTexturesResponse
|
||||
shouldCache := true
|
||||
var uuid string
|
||||
@ -95,7 +98,7 @@ func (ctx *JobsQueue) queueRound() {
|
||||
if shouldCache {
|
||||
// TODO: store result to cache
|
||||
}
|
||||
}()
|
||||
}(job)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
215
api/mojang/queue/queue_test.go
Normal file
215
api/mojang/queue/queue_test.go
Normal file
@ -0,0 +1,215 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
testify "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJobsQueue_GetTexturesForUsername(t *testing.T) {
|
||||
delay = 50 * time.Millisecond
|
||||
|
||||
t.Run("receive textures for one username", func(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
usernamesToUuids = createUsernameToUuidsMock(
|
||||
assert,
|
||||
[]string{"maksimkurb"},
|
||||
[]*mojang.ProfileInfo{
|
||||
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
uuidToTextures = createUuidToTextures([]*createUuidToTexturesResult{
|
||||
createTexturesResult("0d252b7218b648bfb86c2ae476954d32", "maksimkurb"),
|
||||
})
|
||||
|
||||
queue := &JobsQueue{Storage: &NilStorage{}}
|
||||
result := queue.GetTexturesForUsername("maksimkurb")
|
||||
|
||||
if assert.NotNil(result) {
|
||||
assert.Equal("0d252b7218b648bfb86c2ae476954d32", result.Id)
|
||||
assert.Equal("maksimkurb", result.Name)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("receive textures for few usernames", func(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
usernamesToUuids = createUsernameToUuidsMock(
|
||||
assert,
|
||||
[]string{"maksimkurb", "Thinkofdeath"},
|
||||
[]*mojang.ProfileInfo{
|
||||
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
||||
{Id: "4566e69fc90748ee8d71d7ba5aa00d20", Name: "Thinkofdeath"},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
uuidToTextures = createUuidToTextures([]*createUuidToTexturesResult{
|
||||
createTexturesResult("0d252b7218b648bfb86c2ae476954d32", "maksimkurb"),
|
||||
createTexturesResult("4566e69fc90748ee8d71d7ba5aa00d20", "Thinkofdeath"),
|
||||
})
|
||||
|
||||
queue := &JobsQueue{Storage: &NilStorage{}}
|
||||
resultChan1 := make(chan *mojang.SignedTexturesResponse)
|
||||
resultChan2 := make(chan *mojang.SignedTexturesResponse)
|
||||
go func() {
|
||||
resultChan1 <- queue.GetTexturesForUsername("maksimkurb")
|
||||
}()
|
||||
go func() {
|
||||
resultChan2 <- queue.GetTexturesForUsername("Thinkofdeath")
|
||||
}()
|
||||
|
||||
assert.NotNil(<-resultChan1)
|
||||
assert.NotNil(<-resultChan2)
|
||||
})
|
||||
|
||||
t.Run("query no more than 100 usernames and all left on the next iteration", func(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
usernames := make([]string, 120, 120)
|
||||
for i := 0; i < 120; i++ {
|
||||
usernames[i] = randStr(8)
|
||||
}
|
||||
|
||||
usernamesToUuids = createUsernameToUuidsMock(assert, usernames[0:100], []*mojang.ProfileInfo{}, nil)
|
||||
|
||||
queue := &JobsQueue{Storage: &NilStorage{}}
|
||||
|
||||
scheduleUsername := func(username string) {
|
||||
queue.GetTexturesForUsername(username)
|
||||
}
|
||||
|
||||
for _, username := range usernames {
|
||||
go scheduleUsername(username)
|
||||
time.Sleep(50 * time.Microsecond) // Add delay to have consistent order
|
||||
}
|
||||
|
||||
// Let it begin first iteration
|
||||
time.Sleep(delay + delay/2)
|
||||
|
||||
usernamesToUuids = createUsernameToUuidsMock(
|
||||
assert,
|
||||
usernames[100:120],
|
||||
[]*mojang.ProfileInfo{},
|
||||
nil,
|
||||
)
|
||||
|
||||
time.Sleep(delay)
|
||||
})
|
||||
|
||||
t.Run("should do nothing if queue is empty", func(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
usernamesToUuids = createUsernameToUuidsMock(assert, []string{"maksimkurb"}, []*mojang.ProfileInfo{}, nil)
|
||||
uuidToTextures = func(uuid string, signed bool) (*mojang.SignedTexturesResponse, error) {
|
||||
t.Error("this method shouldn't be called")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Perform first iteration and await it finish
|
||||
queue := &JobsQueue{Storage: &NilStorage{}}
|
||||
result := queue.GetTexturesForUsername("maksimkurb")
|
||||
assert.Nil(result)
|
||||
|
||||
// Override external API call that indicates, that queue is still trying to obtain somethid
|
||||
usernamesToUuids = func(usernames []string) ([]*mojang.ProfileInfo, error) {
|
||||
t.Error("this method shouldn't be called")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Let it to iterate few times
|
||||
time.Sleep(delay * 2)
|
||||
})
|
||||
|
||||
t.Run("handle 429 error when exchanging usernames to uuids", func(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
usernamesToUuids = createUsernameToUuidsMock(assert, []string{"maksimkurb"}, nil, &mojang.TooManyRequestsError{})
|
||||
|
||||
queue := &JobsQueue{Storage: &NilStorage{}}
|
||||
result := queue.GetTexturesForUsername("maksimkurb")
|
||||
assert.Nil(result)
|
||||
})
|
||||
|
||||
t.Run("handle 429 error when requesting user's textures", func(t *testing.T) {
|
||||
assert := testify.New(t)
|
||||
|
||||
usernamesToUuids = createUsernameToUuidsMock(
|
||||
assert,
|
||||
[]string{"maksimkurb"},
|
||||
[]*mojang.ProfileInfo{
|
||||
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
uuidToTextures = createUuidToTextures([]*createUuidToTexturesResult{
|
||||
createTexturesResult("0d252b7218b648bfb86c2ae476954d32", &mojang.TooManyRequestsError{}),
|
||||
})
|
||||
|
||||
queue := &JobsQueue{Storage: &NilStorage{}}
|
||||
result := queue.GetTexturesForUsername("maksimkurb")
|
||||
assert.Nil(result)
|
||||
})
|
||||
}
|
||||
|
||||
func createUsernameToUuidsMock(
|
||||
assert *testify.Assertions,
|
||||
expectedUsernames []string,
|
||||
result []*mojang.ProfileInfo,
|
||||
err error,
|
||||
) func(usernames []string) ([]*mojang.ProfileInfo, error) {
|
||||
return func(usernames []string) ([]*mojang.ProfileInfo, error) {
|
||||
assert.ElementsMatch(expectedUsernames, usernames)
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
type createUuidToTexturesResult struct {
|
||||
uuid string
|
||||
result *mojang.SignedTexturesResponse
|
||||
err error
|
||||
}
|
||||
|
||||
func createTexturesResult(uuid string, result interface{}) *createUuidToTexturesResult {
|
||||
output := &createUuidToTexturesResult{uuid: uuid}
|
||||
if username, ok := result.(string); ok {
|
||||
output.result = &mojang.SignedTexturesResponse{Id: uuid, Name: username}
|
||||
} else if err, ok := result.(error); ok {
|
||||
output.err = err
|
||||
} else {
|
||||
log.Fatal("invalid result type passed")
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func createUuidToTextures(
|
||||
results []*createUuidToTexturesResult,
|
||||
) func(uuid string, signed bool) (*mojang.SignedTexturesResponse, error) {
|
||||
return func(uuid string, signed bool) (*mojang.SignedTexturesResponse, error) {
|
||||
for _, result := range results {
|
||||
if result.uuid == uuid {
|
||||
return result.result, result.err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("cannot find corresponding result")
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/50581165
|
||||
func randStr(len int) string {
|
||||
buff := make([]byte, len)
|
||||
rand.Read(buff)
|
||||
str := base64.StdEncoding.EncodeToString(buff)
|
||||
|
||||
// Base 64 can be longer than len
|
||||
return str[:len]
|
||||
}
|
Loading…
Reference in New Issue
Block a user