mirror of
https://github.com/elyby/chrly.git
synced 2024-11-23 05:33:18 +05:30
Integrate event dispatcher into mojangtextures package
This commit is contained in:
parent
b2ee10f72f
commit
2abe2db469
@ -1,6 +1,7 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/elyby/chrly/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
@ -69,7 +70,7 @@ func init() {
|
||||
viper.SetDefault("queue.batch_size", 10)
|
||||
}
|
||||
|
||||
func CreateMojangUUIDsProvider(logger wd.Watchdog) (mojangtextures.UUIDsProvider, error) {
|
||||
func CreateMojangUUIDsProvider(emitter http.Emitter) (mojangtextures.UUIDsProvider, error) {
|
||||
var uuidsProvider mojangtextures.UUIDsProvider
|
||||
preferredUuidsProvider := viper.GetString("mojang_textures.uuids_provider.driver")
|
||||
if preferredUuidsProvider == "remote" {
|
||||
@ -79,14 +80,14 @@ func CreateMojangUUIDsProvider(logger wd.Watchdog) (mojangtextures.UUIDsProvider
|
||||
}
|
||||
|
||||
uuidsProvider = &mojangtextures.RemoteApiUuidsProvider{
|
||||
Url: *remoteUrl,
|
||||
Logger: logger,
|
||||
Emitter: emitter,
|
||||
Url: *remoteUrl,
|
||||
}
|
||||
} else {
|
||||
uuidsProvider = &mojangtextures.BatchUuidsProvider{
|
||||
Emitter: emitter,
|
||||
IterationDelay: viper.GetDuration("queue.loop_delay"),
|
||||
IterationSize: viper.GetInt("queue.batch_size"),
|
||||
Logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ var serveCmd = &cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
uuidsProvider, err := bootstrap.CreateMojangUUIDsProvider(logger)
|
||||
uuidsProvider, err := bootstrap.CreateMojangUUIDsProvider(nil)
|
||||
if err != nil {
|
||||
logger.Emergency("Unable to parse remote url :err", wd.ErrParam(err))
|
||||
return
|
||||
@ -62,10 +62,10 @@ var serveCmd = &cobra.Command{
|
||||
texturesStorage := mojangtextures.NewInMemoryTexturesStorage()
|
||||
texturesStorage.Start()
|
||||
mojangTexturesProvider := &mojangtextures.Provider{
|
||||
Logger: logger,
|
||||
// TODO: configure emitter
|
||||
UUIDsProvider: uuidsProvider,
|
||||
TexturesProvider: &mojangtextures.MojangApiTexturesProvider{
|
||||
Logger: logger,
|
||||
// TODO: configure emitter
|
||||
},
|
||||
Storage: &mojangtextures.SeparatedStorage{
|
||||
UuidsStorage: mojangUuidsRepository,
|
||||
|
@ -31,7 +31,7 @@ var workerCmd = &cobra.Command{
|
||||
address := fmt.Sprintf("%s:%d", viper.GetString("server.host"), viper.GetInt("server.port"))
|
||||
handler := (&http.UUIDsWorker{
|
||||
UUIDsProvider: uuidsProvider,
|
||||
// TODO: create an emitter, restore logger
|
||||
// TODO: configure emitter
|
||||
}).CreateHandler()
|
||||
|
||||
finishChan := make(chan bool)
|
||||
|
@ -226,7 +226,7 @@ func (ctx *Skinsystem) Textures(response http.ResponseWriter, request *http.Requ
|
||||
|
||||
texturesProp := mojangTextures.DecodeTextures()
|
||||
if texturesProp == nil {
|
||||
ctx.Emitter.Emit("skinsystem.error", errors.New("unable to find textures property"))
|
||||
ctx.Emit("skinsystem.error", errors.New("unable to find textures property"))
|
||||
apiServerError(response)
|
||||
return
|
||||
}
|
||||
@ -291,7 +291,7 @@ func (ctx *Skinsystem) PostSkin(resp http.ResponseWriter, req *http.Request) {
|
||||
|
||||
record, err := findIdentity(ctx.SkinsRepo, identityId, username)
|
||||
if err != nil {
|
||||
ctx.Emitter.Emit("skinsystem:error", fmt.Errorf("error on requesting a skin from the repository: %w", err))
|
||||
ctx.Emit("skinsystem:error", fmt.Errorf("error on requesting a skin from the repository: %w", err))
|
||||
apiServerError(resp)
|
||||
return
|
||||
}
|
||||
@ -310,7 +310,7 @@ func (ctx *Skinsystem) PostSkin(resp http.ResponseWriter, req *http.Request) {
|
||||
|
||||
err = ctx.SkinsRepo.Save(record)
|
||||
if err != nil {
|
||||
ctx.Emitter.Emit("skinsystem:error", fmt.Errorf("unable to save record to the repository: %w", err))
|
||||
ctx.Emit("skinsystem:error", fmt.Errorf("unable to save record to the repository: %w", err))
|
||||
apiServerError(resp)
|
||||
return
|
||||
}
|
||||
@ -357,7 +357,7 @@ func (ctx *Skinsystem) deleteSkin(skin *model.Skin, err error, resp http.Respons
|
||||
if _, ok := err.(*SkinNotFoundError); ok {
|
||||
apiNotFound(resp, "Cannot find record for the requested identifier")
|
||||
} else {
|
||||
ctx.Emitter.Emit("skinsystem:error", fmt.Errorf("unable to find skin info from the repository: %w", err))
|
||||
ctx.Emit("skinsystem:error", fmt.Errorf("unable to find skin info from the repository: %w", err))
|
||||
apiServerError(resp)
|
||||
}
|
||||
|
||||
@ -366,7 +366,7 @@ func (ctx *Skinsystem) deleteSkin(skin *model.Skin, err error, resp http.Respons
|
||||
|
||||
err = ctx.SkinsRepo.RemoveByUserId(skin.UserId)
|
||||
if err != nil {
|
||||
ctx.Emitter.Emit("skinsystem:error", fmt.Errorf("cannot delete skin by error: %w", err))
|
||||
ctx.Emit("skinsystem:error", fmt.Errorf("cannot delete skin by error: %w", err))
|
||||
apiServerError(resp)
|
||||
return
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func (ctx *UUIDsWorker) GetUUID(response http.ResponseWriter, request *http.Requ
|
||||
username := parseUsername(mux.Vars(request)["username"])
|
||||
profile, err := ctx.UUIDsProvider.GetUuid(username)
|
||||
if err != nil {
|
||||
ctx.Emitter.Emit("uuids_provider:error", err) // TODO: do I need emitter here?
|
||||
ctx.Emit("uuids_provider:error", err) // TODO: do I need emitter here?
|
||||
if _, ok := err.(*mojang.TooManyRequestsError); ok {
|
||||
response.WriteHeader(http.StatusTooManyRequests)
|
||||
return
|
||||
|
@ -5,8 +5,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mono83/slf/wd"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
)
|
||||
|
||||
@ -68,9 +66,10 @@ var forever = func() bool {
|
||||
}
|
||||
|
||||
type BatchUuidsProvider struct {
|
||||
Emitter
|
||||
|
||||
IterationDelay time.Duration
|
||||
IterationSize int
|
||||
Logger wd.Watchdog
|
||||
|
||||
onFirstCall sync.Once
|
||||
queue jobsQueue
|
||||
@ -84,7 +83,7 @@ func (ctx *BatchUuidsProvider) GetUuid(username string) (*mojang.ProfileInfo, er
|
||||
|
||||
resultChan := make(chan *jobResult)
|
||||
ctx.queue.Enqueue(&jobItem{username, resultChan})
|
||||
ctx.Logger.IncCounter("mojang_textures.usernames.queued", 1)
|
||||
ctx.Emit("mojang_textures:batch_uuids_provider:queued", username)
|
||||
|
||||
result := <-resultChan
|
||||
|
||||
@ -95,10 +94,9 @@ func (ctx *BatchUuidsProvider) startQueue() {
|
||||
go func() {
|
||||
time.Sleep(ctx.IterationDelay)
|
||||
for forever() {
|
||||
start := time.Now()
|
||||
ctx.Emit("mojang_textures:batch_uuids_provider:before_round")
|
||||
ctx.queueRound()
|
||||
elapsed := time.Since(start)
|
||||
ctx.Logger.RecordTimer("mojang_textures.usernames.round_time", elapsed)
|
||||
ctx.Emit("mojang_textures:batch_uuids_provider:after_round")
|
||||
time.Sleep(ctx.IterationDelay)
|
||||
}
|
||||
}()
|
||||
@ -107,17 +105,17 @@ func (ctx *BatchUuidsProvider) startQueue() {
|
||||
func (ctx *BatchUuidsProvider) queueRound() {
|
||||
queueSize := ctx.queue.Size()
|
||||
jobs := ctx.queue.Dequeue(ctx.IterationSize)
|
||||
ctx.Logger.UpdateGauge("mojang_textures.usernames.queue_size", int64(queueSize-len(jobs)))
|
||||
ctx.Logger.UpdateGauge("mojang_textures.usernames.iteration_size", int64(len(jobs)))
|
||||
if len(jobs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var usernames []string
|
||||
for _, job := range jobs {
|
||||
usernames = append(usernames, job.username)
|
||||
}
|
||||
|
||||
ctx.Emit("mojang_textures:batch_uuids_provider:round", usernames, queueSize - len(jobs))
|
||||
if len(usernames) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
profiles, err := usernamesToUuids(usernames)
|
||||
for _, job := range jobs {
|
||||
go func(job *jobItem) {
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
mocks "github.com/elyby/chrly/tests"
|
||||
)
|
||||
|
||||
func TestJobsQueue(t *testing.T) {
|
||||
@ -85,7 +84,7 @@ type batchUuidsProviderTestSuite struct {
|
||||
Provider *BatchUuidsProvider
|
||||
GetUuidAsync func(username string) chan *batchUuidsProviderGetUuidResult
|
||||
|
||||
Logger *mocks.WdMock
|
||||
Emitter *mockEmitter
|
||||
MojangApi *mojangUsernamesToUuidsRequestMock
|
||||
|
||||
Iterate func()
|
||||
@ -94,10 +93,10 @@ type batchUuidsProviderTestSuite struct {
|
||||
}
|
||||
|
||||
func (suite *batchUuidsProviderTestSuite) SetupTest() {
|
||||
suite.Logger = &mocks.WdMock{}
|
||||
suite.Emitter = &mockEmitter{}
|
||||
|
||||
suite.Provider = &BatchUuidsProvider{
|
||||
Logger: suite.Logger,
|
||||
Emitter: suite.Emitter,
|
||||
IterationDelay: 0,
|
||||
IterationSize: 10,
|
||||
}
|
||||
@ -120,7 +119,10 @@ func (suite *batchUuidsProviderTestSuite) SetupTest() {
|
||||
// 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) {
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:batch_uuids_provider:queued",
|
||||
username,
|
||||
).Once().Run(func(args mock.Arguments) {
|
||||
s <- true
|
||||
})
|
||||
|
||||
@ -144,8 +146,8 @@ func (suite *batchUuidsProviderTestSuite) SetupTest() {
|
||||
|
||||
func (suite *batchUuidsProviderTestSuite) TearDownTest() {
|
||||
suite.done()
|
||||
suite.Emitter.AssertExpectations(suite.T())
|
||||
suite.MojangApi.AssertExpectations(suite.T())
|
||||
suite.Logger.AssertExpectations(suite.T())
|
||||
}
|
||||
|
||||
func TestBatchUuidsProvider(t *testing.T) {
|
||||
@ -155,9 +157,9 @@ func TestBatchUuidsProvider(t *testing.T) {
|
||||
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.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:before_round").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", []string{"username"}, 0).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").Once()
|
||||
|
||||
suite.MojangApi.On("UsernamesToUuids", []string{"username"}).Once().Return([]*mojang.ProfileInfo{expectedResult}, nil)
|
||||
|
||||
@ -174,9 +176,9 @@ 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.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:before_round").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", []string{"username1", "username2"}, 0).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").Once()
|
||||
|
||||
suite.MojangApi.On("UsernamesToUuids", []string{"username1", "username2"}).Once().Return([]*mojang.ProfileInfo{
|
||||
expectedResult1,
|
||||
@ -203,18 +205,13 @@ func (suite *batchUuidsProviderTestSuite) TestGetUuidForMoreThan10Usernames() {
|
||||
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.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:round", usernames[10:12], 0).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").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)
|
||||
suite.MojangApi.On("UsernamesToUuids", usernames[0:10]).Once().Return([]*mojang.ProfileInfo{}, nil)
|
||||
suite.MojangApi.On("UsernamesToUuids", usernames[10:12]).Once().Return([]*mojang.ProfileInfo{}, nil)
|
||||
|
||||
channels := make([]chan *batchUuidsProviderGetUuidResult, len(usernames))
|
||||
for i, username := range usernames {
|
||||
@ -230,10 +227,11 @@ func (suite *batchUuidsProviderTestSuite) TestGetUuidForMoreThan10Usernames() {
|
||||
}
|
||||
|
||||
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.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()
|
||||
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)
|
||||
|
||||
@ -254,9 +252,9 @@ func (suite *batchUuidsProviderTestSuite) TestDoNothingWhenNoTasks() {
|
||||
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.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:before_round").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:round", []string{"username1", "username2"}, 0).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:batch_uuids_provider:after_round").Once()
|
||||
|
||||
suite.MojangApi.On("UsernamesToUuids", []string{"username1", "username2"}).Once().Return(nil, expectedError)
|
||||
|
||||
|
@ -1,25 +1,19 @@
|
||||
package mojangtextures
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mono83/slf/wd"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
)
|
||||
|
||||
var uuidToTextures = mojang.UuidToTextures
|
||||
|
||||
type MojangApiTexturesProvider struct {
|
||||
Logger wd.Watchdog
|
||||
Emitter
|
||||
}
|
||||
|
||||
func (ctx *MojangApiTexturesProvider) GetTextures(uuid string) (*mojang.SignedTexturesResponse, error) {
|
||||
ctx.Logger.IncCounter("mojang_textures.textures.request", 1)
|
||||
|
||||
start := time.Now()
|
||||
ctx.Emit("mojang_textures:mojang_api_textures_provider:before_request", uuid)
|
||||
result, err := uuidToTextures(uuid, true)
|
||||
ctx.Logger.RecordTimer("mojang_textures.textures.request_time", time.Since(start))
|
||||
ctx.Emit("mojang_textures:mojang_api_textures_provider:after_request", result, err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
mocks "github.com/elyby/chrly/tests"
|
||||
)
|
||||
|
||||
type mojangUuidToTexturesRequestMock struct {
|
||||
@ -28,16 +27,16 @@ type mojangApiTexturesProviderTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
Provider *MojangApiTexturesProvider
|
||||
Logger *mocks.WdMock
|
||||
Emitter *mockEmitter
|
||||
MojangApi *mojangUuidToTexturesRequestMock
|
||||
}
|
||||
|
||||
func (suite *mojangApiTexturesProviderTestSuite) SetupTest() {
|
||||
suite.Logger = &mocks.WdMock{}
|
||||
suite.Emitter = &mockEmitter{}
|
||||
suite.MojangApi = &mojangUuidToTexturesRequestMock{}
|
||||
|
||||
suite.Provider = &MojangApiTexturesProvider{
|
||||
Logger: suite.Logger,
|
||||
Emitter: suite.Emitter,
|
||||
}
|
||||
|
||||
uuidToTextures = suite.MojangApi.UuidToTextures
|
||||
@ -45,7 +44,7 @@ func (suite *mojangApiTexturesProviderTestSuite) SetupTest() {
|
||||
|
||||
func (suite *mojangApiTexturesProviderTestSuite) TearDownTest() {
|
||||
suite.MojangApi.AssertExpectations(suite.T())
|
||||
suite.Logger.AssertExpectations(suite.T())
|
||||
suite.Emitter.AssertExpectations(suite.T())
|
||||
}
|
||||
|
||||
func TestMojangApiTexturesProvider(t *testing.T) {
|
||||
@ -59,8 +58,15 @@ func (suite *mojangApiTexturesProviderTestSuite) TestGetTextures() {
|
||||
}
|
||||
suite.MojangApi.On("UuidToTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true).Once().Return(expectedResult, nil)
|
||||
|
||||
suite.Logger.On("IncCounter", "mojang_textures.textures.request", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.textures.request_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:mojang_api_textures_provider:before_request",
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
).Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:mojang_api_textures_provider:after_request",
|
||||
expectedResult,
|
||||
nil,
|
||||
).Once()
|
||||
|
||||
result, err := suite.Provider.GetTextures("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
|
||||
@ -69,11 +75,19 @@ func (suite *mojangApiTexturesProviderTestSuite) TestGetTextures() {
|
||||
}
|
||||
|
||||
func (suite *mojangApiTexturesProviderTestSuite) TestGetTexturesWithError() {
|
||||
var expectedResponse *mojang.SignedTexturesResponse
|
||||
expectedError := &mojang.TooManyRequestsError{}
|
||||
suite.MojangApi.On("UuidToTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true).Once().Return(nil, expectedError)
|
||||
|
||||
suite.Logger.On("IncCounter", "mojang_textures.textures.request", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.textures.request_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:mojang_api_textures_provider:before_request",
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
).Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:mojang_api_textures_provider:after_request",
|
||||
expectedResponse,
|
||||
expectedError,
|
||||
).Once()
|
||||
|
||||
result, err := suite.Provider.GetTextures("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
|
||||
|
@ -2,15 +2,9 @@ package mojangtextures
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/mono83/slf/wd"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
)
|
||||
@ -77,40 +71,45 @@ type TexturesProvider interface {
|
||||
GetTextures(uuid string) (*mojang.SignedTexturesResponse, error)
|
||||
}
|
||||
|
||||
type Emitter interface {
|
||||
Emit(name string, args ...interface{})
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
Emitter
|
||||
UUIDsProvider
|
||||
TexturesProvider
|
||||
Storage
|
||||
Logger wd.Watchdog
|
||||
|
||||
onFirstCall sync.Once
|
||||
*broadcaster
|
||||
}
|
||||
|
||||
// TODO: move cache events on the corresponding level
|
||||
|
||||
func (ctx *Provider) GetForUsername(username string) (*mojang.SignedTexturesResponse, error) {
|
||||
ctx.onFirstCall.Do(func() {
|
||||
ctx.broadcaster = createBroadcaster()
|
||||
})
|
||||
|
||||
if !allowedUsernamesRegex.MatchString(username) {
|
||||
ctx.Logger.IncCounter("mojang_textures.invalid_username", 1)
|
||||
return nil, errors.New("invalid username")
|
||||
}
|
||||
|
||||
username = strings.ToLower(username)
|
||||
ctx.Logger.IncCounter("mojang_textures.request", 1)
|
||||
ctx.Emit("mojang_textures:call")
|
||||
|
||||
uuid, err := ctx.Storage.GetUuid(username)
|
||||
if err == nil && uuid == "" {
|
||||
ctx.Logger.IncCounter("mojang_textures.usernames.cache_hit_nil", 1)
|
||||
ctx.Emit("mojang_textures:usernames:cache_hit_nil")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if uuid != "" {
|
||||
ctx.Logger.IncCounter("mojang_textures.usernames.cache_hit", 1)
|
||||
ctx.Emit("mojang_textures:usernames:cache_hit")
|
||||
textures, err := ctx.Storage.GetTextures(uuid)
|
||||
if err == nil {
|
||||
ctx.Logger.IncCounter("mojang_textures.textures.cache_hit", 1)
|
||||
ctx.Emit("mojang_textures:textures:cache_hit")
|
||||
return textures, nil
|
||||
}
|
||||
}
|
||||
@ -120,7 +119,7 @@ func (ctx *Provider) GetForUsername(username string) (*mojang.SignedTexturesResp
|
||||
if isFirstListener {
|
||||
go ctx.getResultAndBroadcast(username, uuid)
|
||||
} else {
|
||||
ctx.Logger.IncCounter("mojang_textures.already_scheduled", 1)
|
||||
ctx.Emit("mojang_textures:already_processing")
|
||||
}
|
||||
|
||||
result := <-resultChan
|
||||
@ -129,19 +128,19 @@ func (ctx *Provider) GetForUsername(username string) (*mojang.SignedTexturesResp
|
||||
}
|
||||
|
||||
func (ctx *Provider) getResultAndBroadcast(username string, uuid string) {
|
||||
start := time.Now()
|
||||
ctx.Emit("mojang_textures:before_get_result")
|
||||
|
||||
result := ctx.getResult(username, uuid)
|
||||
ctx.broadcaster.BroadcastAndRemove(username, result)
|
||||
|
||||
ctx.Logger.RecordTimer("mojang_textures.result_time", time.Since(start))
|
||||
ctx.Emit("mojang_textures:after_get_result")
|
||||
}
|
||||
|
||||
func (ctx *Provider) getResult(username string, uuid string) *broadcastResult {
|
||||
if uuid == "" {
|
||||
profile, err := ctx.UUIDsProvider.GetUuid(username)
|
||||
if err != nil {
|
||||
ctx.handleMojangApiResponseError(err, "usernames")
|
||||
ctx.Emit("mojang_textures:usernames:error", err)
|
||||
return &broadcastResult{nil, err}
|
||||
}
|
||||
|
||||
@ -153,16 +152,16 @@ func (ctx *Provider) getResult(username string, uuid string) *broadcastResult {
|
||||
_ = ctx.Storage.StoreUuid(username, uuid)
|
||||
|
||||
if uuid == "" {
|
||||
ctx.Logger.IncCounter("mojang_textures.usernames.uuid_miss", 1)
|
||||
ctx.Emit("mojang_textures:usernames:uuid_miss")
|
||||
return &broadcastResult{nil, nil}
|
||||
}
|
||||
|
||||
ctx.Logger.IncCounter("mojang_textures.usernames.uuid_hit", 1)
|
||||
ctx.Emit("mojang_textures:usernames:uuid_hit")
|
||||
}
|
||||
|
||||
textures, err := ctx.TexturesProvider.GetTextures(uuid)
|
||||
if err != nil {
|
||||
ctx.handleMojangApiResponseError(err, "textures")
|
||||
ctx.Emit("mojang_textures:textures:error", err)
|
||||
return &broadcastResult{nil, err}
|
||||
}
|
||||
|
||||
@ -171,55 +170,10 @@ func (ctx *Provider) getResult(username string, uuid string) *broadcastResult {
|
||||
ctx.Storage.StoreTextures(uuid, textures)
|
||||
|
||||
if textures != nil {
|
||||
ctx.Logger.IncCounter("mojang_textures.usernames.textures_hit", 1)
|
||||
ctx.Emit("mojang_textures:textures:hit")
|
||||
} else {
|
||||
ctx.Logger.IncCounter("mojang_textures.usernames.textures_miss", 1)
|
||||
ctx.Emit("mojang_textures:textures:miss")
|
||||
}
|
||||
|
||||
return &broadcastResult{textures, nil}
|
||||
}
|
||||
|
||||
func (ctx *Provider) handleMojangApiResponseError(err error, threadName string) {
|
||||
errParam := wd.ErrParam(err)
|
||||
threadParam := wd.NameParam(threadName)
|
||||
|
||||
ctx.Logger.Debug(":name: Got response error :err", threadParam, errParam)
|
||||
|
||||
switch err.(type) {
|
||||
case mojang.ResponseError:
|
||||
if _, ok := err.(*mojang.BadRequestError); ok {
|
||||
ctx.Logger.Warning(":name: Got 400 Bad Request :err", threadParam, errParam)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := err.(*mojang.ForbiddenError); ok {
|
||||
ctx.Logger.Warning(":name: Got 403 Forbidden :err", threadParam, errParam)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := err.(*mojang.TooManyRequestsError); ok {
|
||||
ctx.Logger.Warning(":name: Got 429 Too Many Requests :err", threadParam, errParam)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
case net.Error:
|
||||
if err.(net.Error).Timeout() {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := err.(*url.Error); ok {
|
||||
return
|
||||
}
|
||||
|
||||
if opErr, ok := err.(*net.OpError); ok && (opErr.Op == "dial" || opErr.Op == "read") {
|
||||
return
|
||||
}
|
||||
|
||||
if err == syscall.ECONNREFUSED {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Logger.Emergency(":name: Unknown Mojang response error: :err", threadParam, errParam)
|
||||
}
|
||||
|
@ -2,10 +2,7 @@ package mojangtextures
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -14,7 +11,6 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
mocks "github.com/elyby/chrly/tests"
|
||||
)
|
||||
|
||||
func TestBroadcaster(t *testing.T) {
|
||||
@ -86,6 +82,14 @@ func TestBroadcaster(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
type mockEmitter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (e *mockEmitter) Emit(name string, args ...interface{}) {
|
||||
e.Called(append([]interface{}{name}, args...)...)
|
||||
}
|
||||
|
||||
type mockUuidsProvider struct {
|
||||
mock.Mock
|
||||
}
|
||||
@ -145,32 +149,31 @@ func (m *mockStorage) StoreTextures(uuid string, textures *mojang.SignedTextures
|
||||
type providerTestSuite struct {
|
||||
suite.Suite
|
||||
Provider *Provider
|
||||
Emitter *mockEmitter
|
||||
UuidsProvider *mockUuidsProvider
|
||||
TexturesProvider *mockTexturesProvider
|
||||
Storage *mockStorage
|
||||
Logger *mocks.WdMock
|
||||
}
|
||||
|
||||
func (suite *providerTestSuite) SetupTest() {
|
||||
suite.Emitter = &mockEmitter{}
|
||||
suite.UuidsProvider = &mockUuidsProvider{}
|
||||
suite.TexturesProvider = &mockTexturesProvider{}
|
||||
suite.Storage = &mockStorage{}
|
||||
suite.Logger = &mocks.WdMock{}
|
||||
|
||||
suite.Provider = &Provider{
|
||||
Emitter: suite.Emitter,
|
||||
UUIDsProvider: suite.UuidsProvider,
|
||||
TexturesProvider: suite.TexturesProvider,
|
||||
Storage: suite.Storage,
|
||||
Logger: suite.Logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *providerTestSuite) TearDownTest() {
|
||||
// time.Sleep(10 * time.Millisecond) // Add delay to let finish all goroutines before assert mocks calls
|
||||
suite.Emitter.AssertExpectations(suite.T())
|
||||
suite.UuidsProvider.AssertExpectations(suite.T())
|
||||
suite.TexturesProvider.AssertExpectations(suite.T())
|
||||
suite.Storage.AssertExpectations(suite.T())
|
||||
suite.Logger.AssertExpectations(suite.T())
|
||||
}
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
@ -180,10 +183,11 @@ func TestProvider(t *testing.T) {
|
||||
func (suite *providerTestSuite) TestGetForUsernameWithoutAnyCache() {
|
||||
expectedResult := &mojang.SignedTexturesResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
|
||||
|
||||
suite.Logger.On("IncCounter", "mojang_textures.request", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.uuid_hit", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.textures_hit", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.result_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:before_get_result").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:uuid_hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:textures:hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:after_get_result").Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Once().Return("", &ValueNotFound{})
|
||||
suite.Storage.On("StoreUuid", "username", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil)
|
||||
@ -204,10 +208,11 @@ func (suite *providerTestSuite) TestGetForUsernameWithoutAnyCache() {
|
||||
func (suite *providerTestSuite) TestGetForUsernameWithCachedUuid() {
|
||||
expectedResult := &mojang.SignedTexturesResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
|
||||
|
||||
suite.Logger.On("IncCounter", "mojang_textures.request", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.cache_hit", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.textures_hit", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.result_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:cache_hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:before_get_result").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:textures:hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:after_get_result").Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Once().Return("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", nil)
|
||||
suite.Storage.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil, &ValueNotFound{})
|
||||
@ -224,9 +229,9 @@ func (suite *providerTestSuite) TestGetForUsernameWithCachedUuid() {
|
||||
func (suite *providerTestSuite) TestGetForUsernameWithFullyCachedResult() {
|
||||
expectedResult := &mojang.SignedTexturesResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
|
||||
|
||||
suite.Logger.On("IncCounter", "mojang_textures.request", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.cache_hit", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.textures.cache_hit", int64(1)).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:cache_hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:textures:cache_hit").Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Once().Return("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", nil)
|
||||
suite.Storage.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(expectedResult, nil)
|
||||
@ -238,8 +243,8 @@ func (suite *providerTestSuite) TestGetForUsernameWithFullyCachedResult() {
|
||||
}
|
||||
|
||||
func (suite *providerTestSuite) TestGetForUsernameWithCachedUnknownUuid() {
|
||||
suite.Logger.On("IncCounter", "mojang_textures.request", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.cache_hit_nil", int64(1)).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:cache_hit_nil").Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Once().Return("", nil)
|
||||
|
||||
@ -250,9 +255,10 @@ func (suite *providerTestSuite) TestGetForUsernameWithCachedUnknownUuid() {
|
||||
}
|
||||
|
||||
func (suite *providerTestSuite) TestGetForUsernameWhichHasNoMojangAccount() {
|
||||
suite.Logger.On("IncCounter", "mojang_textures.request", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.uuid_miss", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.result_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:before_get_result").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:uuid_miss").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:after_get_result").Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Once().Return("", &ValueNotFound{})
|
||||
suite.Storage.On("StoreUuid", "username", "").Once().Return(nil)
|
||||
@ -268,10 +274,11 @@ func (suite *providerTestSuite) TestGetForUsernameWhichHasNoMojangAccount() {
|
||||
func (suite *providerTestSuite) TestGetForUsernameWhichHasMojangAccountButHasNoMojangSkin() {
|
||||
var expectedResult *mojang.SignedTexturesResponse
|
||||
|
||||
suite.Logger.On("IncCounter", "mojang_textures.request", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.uuid_hit", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.textures_miss", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.result_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:before_get_result").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:uuid_hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:textures:miss").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:after_get_result").Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Once().Return("", &ValueNotFound{})
|
||||
suite.Storage.On("StoreUuid", "username", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil)
|
||||
@ -292,11 +299,12 @@ func (suite *providerTestSuite) TestGetForUsernameWhichHasMojangAccountButHasNoM
|
||||
func (suite *providerTestSuite) TestGetForTheSameUsernames() {
|
||||
expectedResult := &mojang.SignedTexturesResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
|
||||
|
||||
suite.Logger.On("IncCounter", "mojang_textures.request", int64(1)).Twice()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.already_scheduled", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.uuid_hit", int64(1)).Once()
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.textures_hit", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.result_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Twice()
|
||||
suite.Emitter.On("Emit", "mojang_textures:already_processing").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:before_get_result").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:uuid_hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:textures:hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:after_get_result").Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Twice().Return("", &ValueNotFound{})
|
||||
suite.Storage.On("StoreUuid", "username", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil)
|
||||
@ -326,114 +334,45 @@ func (suite *providerTestSuite) TestGetForTheSameUsernames() {
|
||||
}
|
||||
|
||||
func (suite *providerTestSuite) TestGetForNotAllowedMojangUsername() {
|
||||
suite.Logger.On("IncCounter", "mojang_textures.invalid_username", int64(1)).Once()
|
||||
|
||||
result, err := suite.Provider.GetForUsername("Not allowed")
|
||||
suite.Assert().Error(err, "invalid username")
|
||||
suite.Assert().Nil(result)
|
||||
}
|
||||
|
||||
type timeoutError struct {
|
||||
}
|
||||
func (suite *providerTestSuite) TestGetErrorFromUuidsProvider() {
|
||||
err := errors.New("mock error")
|
||||
|
||||
func (*timeoutError) Error() string { return "timeout error" }
|
||||
func (*timeoutError) Timeout() bool { return true }
|
||||
func (*timeoutError) Temporary() bool { return false }
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:before_get_result").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:error", err).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:after_get_result").Once()
|
||||
|
||||
var expectedErrors = []error{
|
||||
&mojang.BadRequestError{},
|
||||
&mojang.ForbiddenError{},
|
||||
&mojang.TooManyRequestsError{},
|
||||
&mojang.ServerError{},
|
||||
&timeoutError{},
|
||||
&url.Error{Op: "GET", URL: "http://localhost"},
|
||||
&net.OpError{Op: "read"},
|
||||
&net.OpError{Op: "dial"},
|
||||
syscall.ECONNREFUSED,
|
||||
}
|
||||
suite.Storage.On("GetUuid", "username").Once().Return("", &ValueNotFound{})
|
||||
suite.UuidsProvider.On("GetUuid", "username").Once().Return(nil, err)
|
||||
|
||||
func (suite *providerTestSuite) TestShouldNotLogErrorWhenExpectedErrorReturnedFromUsernameToUuidRequest() {
|
||||
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
||||
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
||||
suite.Logger.On("Debug", ":name: Got response error :err", mock.Anything, mock.Anything).Times(len(expectedErrors))
|
||||
suite.Logger.On("Warning", ":name: Got 400 Bad Request :err", mock.Anything, mock.Anything).Once()
|
||||
suite.Logger.On("Warning", ":name: Got 403 Forbidden :err", mock.Anything, mock.Anything).Once()
|
||||
suite.Logger.On("Warning", ":name: Got 429 Too Many Requests :err", mock.Anything, mock.Anything).Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Return("", &ValueNotFound{})
|
||||
|
||||
for _, err := range expectedErrors {
|
||||
suite.UuidsProvider.On("GetUuid", "username").Once().Return(nil, err)
|
||||
|
||||
result, err := suite.Provider.GetForUsername("username")
|
||||
suite.Assert().Nil(result)
|
||||
suite.Assert().NotNil(err)
|
||||
suite.UuidsProvider.AssertExpectations(suite.T())
|
||||
suite.UuidsProvider.ExpectedCalls = nil // https://github.com/stretchr/testify/issues/558#issuecomment-372112364
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *providerTestSuite) TestShouldLogEmergencyOnUnexpectedErrorReturnedFromUsernameToUuidRequest() {
|
||||
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
||||
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
||||
suite.Logger.On("Debug", ":name: Got response error :err", mock.Anything, mock.Anything).Once()
|
||||
suite.Logger.On("Emergency", ":name: Unknown Mojang response error: :err", mock.Anything, mock.Anything).Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Return("", &ValueNotFound{})
|
||||
|
||||
suite.UuidsProvider.On("GetUuid", "username").Once().Return(nil, errors.New("unexpected error"))
|
||||
|
||||
result, err := suite.Provider.GetForUsername("username")
|
||||
result, resErr := suite.Provider.GetForUsername("username")
|
||||
suite.Assert().Nil(result)
|
||||
suite.Assert().NotNil(err)
|
||||
suite.Assert().Equal(err, resErr)
|
||||
}
|
||||
|
||||
func (suite *providerTestSuite) TestShouldNotLogErrorWhenExpectedErrorReturnedFromUuidToTexturesRequest() {
|
||||
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
||||
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
||||
suite.Logger.On("Debug", ":name: Got response error :err", mock.Anything, mock.Anything).Times(len(expectedErrors))
|
||||
suite.Logger.On("Warning", ":name: Got 400 Bad Request :err", mock.Anything, mock.Anything).Once()
|
||||
suite.Logger.On("Warning", ":name: Got 403 Forbidden :err", mock.Anything, mock.Anything).Once()
|
||||
suite.Logger.On("Warning", ":name: Got 429 Too Many Requests :err", mock.Anything, mock.Anything).Once()
|
||||
func (suite *providerTestSuite) TestGetErrorFromTexturesProvider() {
|
||||
err := errors.New("mock error")
|
||||
|
||||
suite.Emitter.On("Emit", "mojang_textures:call").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:before_get_result").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:usernames:uuid_hit").Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:textures:error", err).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:after_get_result").Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Return("", &ValueNotFound{})
|
||||
suite.Storage.On("StoreUuid", "username", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Return(nil)
|
||||
// suite.Storage.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Return(nil, &ValueNotFound{})
|
||||
// suite.Storage.On("StoreTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", (*mojang.SignedTexturesResponse)(nil))
|
||||
|
||||
for _, err := range expectedErrors {
|
||||
suite.UuidsProvider.On("GetUuid", "username").Once().Return(&mojang.ProfileInfo{
|
||||
Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
Name: "username",
|
||||
}, nil)
|
||||
suite.TexturesProvider.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil, err)
|
||||
|
||||
result, err := suite.Provider.GetForUsername("username")
|
||||
suite.Assert().Nil(result)
|
||||
suite.Assert().NotNil(err)
|
||||
suite.UuidsProvider.AssertExpectations(suite.T())
|
||||
suite.TexturesProvider.AssertExpectations(suite.T())
|
||||
suite.UuidsProvider.ExpectedCalls = nil // https://github.com/stretchr/testify/issues/558#issuecomment-372112364
|
||||
suite.TexturesProvider.ExpectedCalls = nil // https://github.com/stretchr/testify/issues/558#issuecomment-372112364
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *providerTestSuite) TestShouldLogEmergencyOnUnexpectedErrorReturnedFromUuidToTexturesRequest() {
|
||||
suite.Logger.On("IncCounter", mock.Anything, mock.Anything)
|
||||
suite.Logger.On("RecordTimer", mock.Anything, mock.Anything)
|
||||
suite.Logger.On("Debug", ":name: Got response error :err", mock.Anything, mock.Anything).Once()
|
||||
suite.Logger.On("Emergency", ":name: Unknown Mojang response error: :err", mock.Anything, mock.Anything).Once()
|
||||
|
||||
suite.Storage.On("GetUuid", "username").Return("", &ValueNotFound{})
|
||||
suite.Storage.On("StoreUuid", "username", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Return(nil)
|
||||
|
||||
suite.UuidsProvider.On("GetUuid", "username").Once().Return(&mojang.ProfileInfo{
|
||||
Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
Name: "username",
|
||||
}, nil)
|
||||
suite.TexturesProvider.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil, errors.New("unexpected error"))
|
||||
suite.TexturesProvider.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil, err)
|
||||
|
||||
result, err := suite.Provider.GetForUsername("username")
|
||||
result, resErr := suite.Provider.GetForUsername("username")
|
||||
suite.Assert().Nil(result)
|
||||
suite.Assert().NotNil(err)
|
||||
suite.Assert().Equal(err, resErr)
|
||||
}
|
||||
|
@ -2,16 +2,13 @@ package mojangtextures
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/elyby/chrly/version"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
. "net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/mono83/slf/wd"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
"github.com/elyby/chrly/version"
|
||||
)
|
||||
|
||||
var HttpClient = &http.Client{
|
||||
@ -21,24 +18,23 @@ var HttpClient = &http.Client{
|
||||
}
|
||||
|
||||
type RemoteApiUuidsProvider struct {
|
||||
Emitter
|
||||
Url URL
|
||||
Logger wd.Watchdog
|
||||
}
|
||||
|
||||
func (ctx *RemoteApiUuidsProvider) GetUuid(username string) (*mojang.ProfileInfo, error) {
|
||||
ctx.Logger.IncCounter("mojang_textures.usernames.request", 1)
|
||||
|
||||
url := ctx.Url
|
||||
url.Path = path.Join(url.Path, username)
|
||||
urlStr := url.String()
|
||||
|
||||
request, _ := http.NewRequest("GET", url.String(), nil)
|
||||
request, _ := http.NewRequest("GET", urlStr, nil)
|
||||
request.Header.Add("Accept", "application/json")
|
||||
// Change default User-Agent to allow specify "Username -> UUID at time" Mojang's api endpoint
|
||||
request.Header.Add("User-Agent", "Chrly/"+version.Version())
|
||||
|
||||
start := time.Now()
|
||||
ctx.Emit("mojang_textures:remote_api_uuids_provider:before_request", urlStr)
|
||||
response, err := HttpClient.Do(request)
|
||||
ctx.Logger.RecordTimer("mojang_textures.usernames.request_time", time.Since(start))
|
||||
ctx.Emit("mojang_textures:remote_api_uuids_provider:after_request", response, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -3,21 +3,19 @@ package mojangtextures
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
. "net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/h2non/gock"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
mocks "github.com/elyby/chrly/tests"
|
||||
)
|
||||
|
||||
type remoteApiUuidsProviderTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
Provider *RemoteApiUuidsProvider
|
||||
Logger *mocks.WdMock
|
||||
Emitter *mockEmitter
|
||||
}
|
||||
|
||||
func (suite *remoteApiUuidsProviderTestSuite) SetupSuite() {
|
||||
@ -28,14 +26,14 @@ func (suite *remoteApiUuidsProviderTestSuite) SetupSuite() {
|
||||
}
|
||||
|
||||
func (suite *remoteApiUuidsProviderTestSuite) SetupTest() {
|
||||
suite.Logger = &mocks.WdMock{}
|
||||
suite.Emitter = &mockEmitter{}
|
||||
suite.Provider = &RemoteApiUuidsProvider{
|
||||
Logger: suite.Logger,
|
||||
Emitter: suite.Emitter,
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *remoteApiUuidsProviderTestSuite) TearDownTest() {
|
||||
suite.Logger.AssertExpectations(suite.T())
|
||||
suite.Emitter.AssertExpectations(suite.T())
|
||||
gock.Off()
|
||||
}
|
||||
|
||||
@ -44,8 +42,12 @@ func TestRemoteApiUuidsProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForValidUsername() {
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.request", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.usernames.request_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:remote_api_uuids_provider:before_request", "http://example.com/subpath/username").Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:remote_api_uuids_provider:after_request",
|
||||
mock.AnythingOfType("*http.Response"),
|
||||
nil,
|
||||
).Once()
|
||||
|
||||
gock.New("http://example.com").
|
||||
Get("/subpath/username").
|
||||
@ -68,8 +70,12 @@ func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForValidUsername() {
|
||||
}
|
||||
|
||||
func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForNotExistsUsername() {
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.request", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.usernames.request_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:remote_api_uuids_provider:before_request", "http://example.com/subpath/username").Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:remote_api_uuids_provider:after_request",
|
||||
mock.AnythingOfType("*http.Response"),
|
||||
nil,
|
||||
).Once()
|
||||
|
||||
gock.New("http://example.com").
|
||||
Get("/subpath/username").
|
||||
@ -84,8 +90,12 @@ func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForNotExistsUsername()
|
||||
}
|
||||
|
||||
func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForNon20xResponse() {
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.request", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.usernames.request_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:remote_api_uuids_provider:before_request", "http://example.com/subpath/username").Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:remote_api_uuids_provider:after_request",
|
||||
mock.AnythingOfType("*http.Response"),
|
||||
nil,
|
||||
).Once()
|
||||
|
||||
gock.New("http://example.com").
|
||||
Get("/subpath/username").
|
||||
@ -101,8 +111,12 @@ func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForNon20xResponse() {
|
||||
}
|
||||
|
||||
func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForNotSuccessRequest() {
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.request", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.usernames.request_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:remote_api_uuids_provider:before_request", "http://example.com/subpath/username").Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:remote_api_uuids_provider:after_request",
|
||||
mock.AnythingOfType("*http.Response"),
|
||||
mock.AnythingOfType("*url.Error"),
|
||||
).Once()
|
||||
|
||||
expectedError := &net.OpError{Op: "dial"}
|
||||
|
||||
@ -116,15 +130,19 @@ func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForNotSuccessRequest()
|
||||
assert := suite.Assert()
|
||||
assert.Nil(result)
|
||||
if assert.Error(err) {
|
||||
assert.IsType(&url.Error{}, err)
|
||||
casterErr, _ := err.(*url.Error)
|
||||
assert.IsType(&Error{}, err)
|
||||
casterErr, _ := err.(*Error)
|
||||
assert.Equal(expectedError, casterErr.Err)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForInvalidSuccessResponse() {
|
||||
suite.Logger.On("IncCounter", "mojang_textures.usernames.request", int64(1)).Once()
|
||||
suite.Logger.On("RecordTimer", "mojang_textures.usernames.request_time", mock.Anything).Once()
|
||||
suite.Emitter.On("Emit", "mojang_textures:remote_api_uuids_provider:before_request", "http://example.com/subpath/username").Once()
|
||||
suite.Emitter.On("Emit",
|
||||
"mojang_textures:remote_api_uuids_provider:after_request",
|
||||
mock.AnythingOfType("*http.Response"),
|
||||
nil,
|
||||
).Once()
|
||||
|
||||
gock.New("http://example.com").
|
||||
Get("/subpath/username").
|
||||
@ -139,8 +157,8 @@ func (suite *remoteApiUuidsProviderTestSuite) TestGetUuidForInvalidSuccessRespon
|
||||
assert.Error(err)
|
||||
}
|
||||
|
||||
func shouldParseUrl(rawUrl string) url.URL {
|
||||
url, err := url.Parse(rawUrl)
|
||||
func shouldParseUrl(rawUrl string) URL {
|
||||
url, err := Parse(rawUrl)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user