Integrate event dispatcher into mojangtextures package

This commit is contained in:
ErickSkrauch 2020-02-08 13:28:10 +03:00
parent b2ee10f72f
commit 2abe2db469
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
13 changed files with 207 additions and 295 deletions

View File

@ -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,
}
}

View File

@ -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,

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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) {

View File

@ -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)

View File

@ -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
}

View File

@ -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")

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}