Make Mojang profiles provider cancellable

This commit is contained in:
ErickSkrauch
2024-02-07 01:36:18 +01:00
parent 10c11bc060
commit bc4d714112
12 changed files with 320 additions and 204 deletions

View File

@@ -1,6 +1,7 @@
package http package http
import ( import (
"context"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
@@ -20,7 +21,7 @@ import (
var timeNow = time.Now var timeNow = time.Now
type ProfilesProvider interface { type ProfilesProvider interface {
FindProfileByUsername(username string, allowProxy bool) (*db.Profile, error) FindProfileByUsername(ctx context.Context, username string, allowProxy bool) (*db.Profile, error)
} }
type TexturesSigner interface { type TexturesSigner interface {
@@ -55,7 +56,7 @@ func (ctx *Skinsystem) Handler() *mux.Router {
} }
func (ctx *Skinsystem) skinHandler(response http.ResponseWriter, request *http.Request) { func (ctx *Skinsystem) skinHandler(response http.ResponseWriter, request *http.Request) {
profile, err := ctx.ProfilesProvider.FindProfileByUsername(parseUsername(mux.Vars(request)["username"]), true) profile, err := ctx.ProfilesProvider.FindProfileByUsername(request.Context(), parseUsername(mux.Vars(request)["username"]), true)
if err != nil { if err != nil {
apiServerError(response, "Unable to retrieve a skin", err) apiServerError(response, "Unable to retrieve a skin", err)
return return
@@ -82,7 +83,7 @@ func (ctx *Skinsystem) skinGetHandler(response http.ResponseWriter, request *htt
} }
func (ctx *Skinsystem) capeHandler(response http.ResponseWriter, request *http.Request) { func (ctx *Skinsystem) capeHandler(response http.ResponseWriter, request *http.Request) {
profile, err := ctx.ProfilesProvider.FindProfileByUsername(parseUsername(mux.Vars(request)["username"]), true) profile, err := ctx.ProfilesProvider.FindProfileByUsername(request.Context(), parseUsername(mux.Vars(request)["username"]), true)
if err != nil { if err != nil {
apiServerError(response, "Unable to retrieve a cape", err) apiServerError(response, "Unable to retrieve a cape", err)
return return
@@ -109,7 +110,7 @@ func (ctx *Skinsystem) capeGetHandler(response http.ResponseWriter, request *htt
} }
func (ctx *Skinsystem) texturesHandler(response http.ResponseWriter, request *http.Request) { func (ctx *Skinsystem) texturesHandler(response http.ResponseWriter, request *http.Request) {
profile, err := ctx.ProfilesProvider.FindProfileByUsername(mux.Vars(request)["username"], true) profile, err := ctx.ProfilesProvider.FindProfileByUsername(request.Context(), mux.Vars(request)["username"], true)
if err != nil { if err != nil {
apiServerError(response, "Unable to retrieve a profile", err) apiServerError(response, "Unable to retrieve a profile", err)
return return
@@ -134,6 +135,7 @@ func (ctx *Skinsystem) texturesHandler(response http.ResponseWriter, request *ht
func (ctx *Skinsystem) signedTexturesHandler(response http.ResponseWriter, request *http.Request) { func (ctx *Skinsystem) signedTexturesHandler(response http.ResponseWriter, request *http.Request) {
profile, err := ctx.ProfilesProvider.FindProfileByUsername( profile, err := ctx.ProfilesProvider.FindProfileByUsername(
request.Context(),
mux.Vars(request)["username"], mux.Vars(request)["username"],
getToBool(request.URL.Query().Get("proxy")), getToBool(request.URL.Query().Get("proxy")),
) )
@@ -174,7 +176,7 @@ func (ctx *Skinsystem) signedTexturesHandler(response http.ResponseWriter, reque
} }
func (ctx *Skinsystem) profileHandler(response http.ResponseWriter, request *http.Request) { func (ctx *Skinsystem) profileHandler(response http.ResponseWriter, request *http.Request) {
profile, err := ctx.ProfilesProvider.FindProfileByUsername(mux.Vars(request)["username"], true) profile, err := ctx.ProfilesProvider.FindProfileByUsername(request.Context(), mux.Vars(request)["username"], true)
if err != nil { if err != nil {
apiServerError(response, "Unable to retrieve a profile", err) apiServerError(response, "Unable to retrieve a profile", err)
return return

View File

@@ -1,6 +1,7 @@
package http package http
import ( import (
"context"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
@@ -23,8 +24,8 @@ type ProfilesProviderMock struct {
mock.Mock mock.Mock
} }
func (m *ProfilesProviderMock) FindProfileByUsername(username string, allowProxy bool) (*db.Profile, error) { func (m *ProfilesProviderMock) FindProfileByUsername(ctx context.Context, username string, allowProxy bool) (*db.Profile, error) {
args := m.Called(username, allowProxy) args := m.Called(ctx, username, allowProxy)
var result *db.Profile var result *db.Profile
if casted, ok := args.Get(0).(*db.Profile); ok { if casted, ok := args.Get(0).(*db.Profile); ok {
result = casted result = casted
@@ -90,12 +91,14 @@ func (t *SkinsystemTestSuite) TearDownSubTest() {
func (t *SkinsystemTestSuite) TestSkinHandler() { func (t *SkinsystemTestSuite) TestSkinHandler() {
for _, url := range []string{"http://chrly/skins/mock_username", "http://chrly/skins?name=mock_username"} { for _, url := range []string{"http://chrly/skins/mock_username", "http://chrly/skins?name=mock_username"} {
t.Run("known username with a skin", func() { t.Run("known username with a skin", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{
SkinUrl: "https://example.com/skin.png",
}, nil)
req := httptest.NewRequest("GET", url, nil) req := httptest.NewRequest("GET", url, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
// TODO: see the TODO about context above
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
SkinUrl: "https://example.com/skin.png",
}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -104,10 +107,11 @@ func (t *SkinsystemTestSuite) TestSkinHandler() {
}) })
t.Run("known username without a skin", func() { t.Run("known username without a skin", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{}, nil)
req := httptest.NewRequest("GET", url, nil) req := httptest.NewRequest("GET", url, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -115,10 +119,11 @@ func (t *SkinsystemTestSuite) TestSkinHandler() {
}) })
t.Run("err from profiles provider", func() { t.Run("err from profiles provider", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(nil, errors.New("mock error"))
req := httptest.NewRequest("GET", url, nil) req := httptest.NewRequest("GET", url, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(nil, errors.New("mock error"))
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -127,12 +132,13 @@ func (t *SkinsystemTestSuite) TestSkinHandler() {
} }
t.Run("username with png extension", func() { t.Run("username with png extension", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{
SkinUrl: "https://example.com/skin.png",
}, nil)
req := httptest.NewRequest("GET", "http://chrly/skins/mock_username.png", nil) req := httptest.NewRequest("GET", "http://chrly/skins/mock_username.png", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
SkinUrl: "https://example.com/skin.png",
}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -154,12 +160,16 @@ func (t *SkinsystemTestSuite) TestSkinHandler() {
func (t *SkinsystemTestSuite) TestCapeHandler() { func (t *SkinsystemTestSuite) TestCapeHandler() {
for _, url := range []string{"http://chrly/cloaks/mock_username", "http://chrly/cloaks?name=mock_username"} { for _, url := range []string{"http://chrly/cloaks/mock_username", "http://chrly/cloaks?name=mock_username"} {
t.Run("known username with a skin", func() { t.Run("known username with a skin", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{
CapeUrl: "https://example.com/cape.png",
}, nil)
req := httptest.NewRequest("GET", url, nil) req := httptest.NewRequest("GET", url, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
// TODO: I can't find a way to verify that it's the context from the request that was passed in,
// as the Mux calls WithValue() on it, which creates a new Context and I haven't been able
// to find a way to verify that the passed context matches the base
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
CapeUrl: "https://example.com/cape.png",
}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -168,10 +178,11 @@ func (t *SkinsystemTestSuite) TestCapeHandler() {
}) })
t.Run("known username without a skin", func() { t.Run("known username without a skin", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{}, nil)
req := httptest.NewRequest("GET", url, nil) req := httptest.NewRequest("GET", url, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -179,10 +190,11 @@ func (t *SkinsystemTestSuite) TestCapeHandler() {
}) })
t.Run("err from profiles provider", func() { t.Run("err from profiles provider", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(nil, errors.New("mock error"))
req := httptest.NewRequest("GET", url, nil) req := httptest.NewRequest("GET", url, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(nil, errors.New("mock error"))
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -191,12 +203,13 @@ func (t *SkinsystemTestSuite) TestCapeHandler() {
} }
t.Run("username with png extension", func() { t.Run("username with png extension", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{
CapeUrl: "https://example.com/cape.png",
}, nil)
req := httptest.NewRequest("GET", "http://chrly/cloaks/mock_username.png", nil) req := httptest.NewRequest("GET", "http://chrly/cloaks/mock_username.png", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
CapeUrl: "https://example.com/cape.png",
}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -217,12 +230,14 @@ func (t *SkinsystemTestSuite) TestCapeHandler() {
func (t *SkinsystemTestSuite) TestTexturesHandler() { func (t *SkinsystemTestSuite) TestTexturesHandler() {
t.Run("known username with both textures", func() { t.Run("known username with both textures", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{ req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder()
// TODO: see the TODO about context above
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
SkinUrl: "https://example.com/skin.png", SkinUrl: "https://example.com/skin.png",
CapeUrl: "https://example.com/cape.png", CapeUrl: "https://example.com/cape.png",
}, nil) }, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder()
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
@@ -241,12 +256,13 @@ func (t *SkinsystemTestSuite) TestTexturesHandler() {
}) })
t.Run("known username with only slim skin", func() { t.Run("known username with only slim skin", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{ req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
SkinUrl: "https://example.com/skin.png", SkinUrl: "https://example.com/skin.png",
SkinModel: "slim", SkinModel: "slim",
}, nil) }, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder()
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
@@ -263,12 +279,13 @@ func (t *SkinsystemTestSuite) TestTexturesHandler() {
}) })
t.Run("known username with only cape", func() { t.Run("known username with only cape", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{
CapeUrl: "https://example.com/cape.png",
}, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
CapeUrl: "https://example.com/cape.png",
}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -281,10 +298,11 @@ func (t *SkinsystemTestSuite) TestTexturesHandler() {
}) })
t.Run("known username without any textures", func() { t.Run("known username without any textures", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{}, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -294,10 +312,11 @@ func (t *SkinsystemTestSuite) TestTexturesHandler() {
}) })
t.Run("unknown username", func() { t.Run("unknown username", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(nil, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(nil, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -307,10 +326,11 @@ func (t *SkinsystemTestSuite) TestTexturesHandler() {
}) })
t.Run("err from profiles provider", func() { t.Run("err from profiles provider", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(nil, errors.New("mock error"))
req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(nil, errors.New("mock error"))
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -318,24 +338,18 @@ func (t *SkinsystemTestSuite) TestTexturesHandler() {
}) })
} }
type signedTexturesTestCase struct {
Name string
AllowProxy bool
BeforeTest func(suite *SkinsystemTestSuite)
PanicErr string
AfterTest func(suite *SkinsystemTestSuite, response *http.Response)
}
func (t *SkinsystemTestSuite) TestSignedTextures() { func (t *SkinsystemTestSuite) TestSignedTextures() {
t.Run("exists profile with mojang textures", func() { t.Run("exists profile with mojang textures", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", false).Return(&db.Profile{ req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username", nil)
w := httptest.NewRecorder()
// TODO: see the TODO about context above
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", false).Return(&db.Profile{
Uuid: "mock-uuid", Uuid: "mock-uuid",
Username: "mock", Username: "mock",
MojangTextures: "mock-mojang-textures", MojangTextures: "mock-mojang-textures",
MojangSignature: "mock-mojang-signature", MojangSignature: "mock-mojang-signature",
}, nil) }, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username", nil)
w := httptest.NewRecorder()
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
@@ -361,10 +375,11 @@ func (t *SkinsystemTestSuite) TestSignedTextures() {
}) })
t.Run("exists profile without mojang textures", func() { t.Run("exists profile without mojang textures", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", false).Return(&db.Profile{}, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", false).Return(&db.Profile{}, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -374,10 +389,11 @@ func (t *SkinsystemTestSuite) TestSignedTextures() {
}) })
t.Run("not exists profile", func() { t.Run("not exists profile", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", false).Return(nil, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", false).Return(nil, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -387,10 +403,11 @@ func (t *SkinsystemTestSuite) TestSignedTextures() {
}) })
t.Run("err from profiles provider", func() { t.Run("err from profiles provider", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", false).Return(nil, errors.New("mock error"))
req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", false).Return(nil, errors.New("mock error"))
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -398,25 +415,28 @@ func (t *SkinsystemTestSuite) TestSignedTextures() {
}) })
t.Run("should allow proxying when specified get param", func() { t.Run("should allow proxying when specified get param", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(nil, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username?proxy=true", nil) req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_username?proxy=true", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(nil, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
}) })
} }
func (t *SkinsystemTestSuite) TestProfile() { func (t *SkinsystemTestSuite) TestProfile() {
t.Run("exists profile with skin and cape", func() { t.Run("exists profile with skin and cape", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{ req := httptest.NewRequest("GET", "http://chrly/profile/mock_username", nil)
w := httptest.NewRecorder()
// TODO: see the TODO about context above
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
Uuid: "mock-uuid", Uuid: "mock-uuid",
Username: "mock_username", Username: "mock_username",
SkinUrl: "https://example.com/skin.png", SkinUrl: "https://example.com/skin.png",
SkinModel: "slim", SkinModel: "slim",
CapeUrl: "https://example.com/cape.png", CapeUrl: "https://example.com/cape.png",
}, nil) }, nil)
req := httptest.NewRequest("GET", "http://chrly/profile/mock_username", nil)
w := httptest.NewRecorder()
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
@@ -441,15 +461,16 @@ func (t *SkinsystemTestSuite) TestProfile() {
}) })
t.Run("exists signed profile with skin", func() { t.Run("exists signed profile with skin", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{ req := httptest.NewRequest("GET", "http://chrly/profile/mock_username?unsigned=false", nil)
w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{
Uuid: "mock-uuid", Uuid: "mock-uuid",
Username: "mock_username", Username: "mock_username",
SkinUrl: "https://example.com/skin.png", SkinUrl: "https://example.com/skin.png",
SkinModel: "slim", SkinModel: "slim",
}, nil) }, nil)
t.TexturesSigner.On("SignTextures", "eyJ0aW1lc3RhbXAiOjE2MTQyMTQyMjMwMDAsInByb2ZpbGVJZCI6Im1vY2stdXVpZCIsInByb2ZpbGVOYW1lIjoibW9ja191c2VybmFtZSIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9za2luLnBuZyIsIm1ldGFkYXRhIjp7Im1vZGVsIjoic2xpbSJ9fX19").Return("mock signature", nil) t.TexturesSigner.On("SignTextures", "eyJ0aW1lc3RhbXAiOjE2MTQyMTQyMjMwMDAsInByb2ZpbGVJZCI6Im1vY2stdXVpZCIsInByb2ZpbGVOYW1lIjoibW9ja191c2VybmFtZSIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9za2luLnBuZyIsIm1ldGFkYXRhIjp7Im1vZGVsIjoic2xpbSJ9fX19").Return("mock signature", nil)
req := httptest.NewRequest("GET", "http://chrly/profile/mock_username?unsigned=false", nil)
w := httptest.NewRecorder()
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
@@ -475,10 +496,11 @@ func (t *SkinsystemTestSuite) TestProfile() {
}) })
t.Run("not exists profile", func() { t.Run("not exists profile", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(nil, nil)
req := httptest.NewRequest("GET", "http://chrly/profile/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/profile/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(nil, nil)
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -488,10 +510,11 @@ func (t *SkinsystemTestSuite) TestProfile() {
}) })
t.Run("err from profiles provider", func() { t.Run("err from profiles provider", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(nil, errors.New("mock error"))
req := httptest.NewRequest("GET", "http://chrly/profile/mock_username", nil) req := httptest.NewRequest("GET", "http://chrly/profile/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(nil, errors.New("mock error"))
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()
@@ -499,11 +522,12 @@ func (t *SkinsystemTestSuite) TestProfile() {
}) })
t.Run("err from textures signer", func() { t.Run("err from textures signer", func() {
t.ProfilesProvider.On("FindProfileByUsername", "mock_username", true).Return(&db.Profile{}, nil)
t.TexturesSigner.On("SignTextures", mock.Anything).Return("", errors.New("mock error"))
req := httptest.NewRequest("GET", "http://chrly/profile/mock_username?unsigned=false", nil) req := httptest.NewRequest("GET", "http://chrly/profile/mock_username?unsigned=false", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{}, nil)
t.TexturesSigner.On("SignTextures", mock.Anything).Return("", errors.New("mock error"))
t.App.Handler().ServeHTTP(w, req) t.App.Handler().ServeHTTP(w, req)
result := w.Result() result := w.Result()

View File

@@ -1,6 +1,7 @@
package mojang package mojang
import ( import (
"context"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -39,6 +40,7 @@ func NewBatchUuidsProvider(
type job struct { type job struct {
Username string Username string
Ctx context.Context
ResultChan chan<- *jobResult ResultChan chan<- *jobResult
} }
@@ -47,43 +49,68 @@ type jobResult struct {
Error error Error error
} }
func (ctx *BatchUuidsProvider) GetUuid(username string) (*ProfileInfo, error) { func (p *BatchUuidsProvider) GetUuid(ctx context.Context, username string) (*ProfileInfo, error) {
resultChan := make(chan *jobResult) resultChan := make(chan *jobResult)
n := ctx.queue.Enqueue(&job{username, resultChan}) n := p.queue.Enqueue(&job{username, ctx, resultChan})
if ctx.fireOnFull && n%ctx.batch == 0 { if p.fireOnFull && n%p.batch == 0 {
ctx.fireChan <- struct{}{} p.fireChan <- struct{}{}
} }
ctx.onFirstCall.Do(ctx.startQueue) p.onFirstCall.Do(p.startQueue)
result := <-resultChan select {
case <-ctx.Done():
return result.Profile, result.Error return nil, ctx.Err()
case result := <-resultChan:
return result.Profile, result.Error
}
} }
func (ctx *BatchUuidsProvider) StopQueue() { func (p *BatchUuidsProvider) StopQueue() {
close(ctx.stopChan) close(p.stopChan)
} }
func (ctx *BatchUuidsProvider) startQueue() { func (p *BatchUuidsProvider) startQueue() {
go func() { go func() {
for { for {
t := time.NewTimer(ctx.delay) t := time.NewTimer(p.delay)
select { select {
case <-ctx.stopChan: case <-p.stopChan:
return return
case <-t.C: case <-t.C:
go ctx.fireRequest() go p.fireRequest()
case <-ctx.fireChan: case <-p.fireChan:
t.Stop() t.Stop()
go ctx.fireRequest() go p.fireRequest()
} }
} }
}() }()
} }
func (ctx *BatchUuidsProvider) fireRequest() { func (p *BatchUuidsProvider) fireRequest() {
jobs, _ := ctx.queue.Dequeue(ctx.batch) jobs := make([]*job, 0, p.batch)
n := p.batch
for {
foundJobs, left := p.queue.Dequeue(n)
for i := range foundJobs {
if foundJobs[i].Ctx.Err() != nil {
// If the job context has already ended, its result will be returned in the GetUuid method
close(foundJobs[i].ResultChan)
foundJobs[i] = foundJobs[len(foundJobs)-1]
foundJobs = foundJobs[:len(foundJobs)-1]
}
}
jobs = append(jobs, foundJobs...)
if len(jobs) != p.batch && left != 0 {
n = p.batch - len(jobs)
continue
}
break
}
if len(jobs) == 0 { if len(jobs) == 0 {
return return
} }
@@ -93,7 +120,7 @@ func (ctx *BatchUuidsProvider) fireRequest() {
usernames[i] = job.Username usernames[i] = job.Username
} }
profiles, err := ctx.UsernamesToUuidsEndpoint(usernames) profiles, err := p.UsernamesToUuidsEndpoint(usernames)
for _, job := range jobs { for _, job := range jobs {
response := &jobResult{} response := &jobResult{}
if err == nil { if err == nil {

View File

@@ -1,6 +1,7 @@
package mojang package mojang
import ( import (
"context"
"errors" "errors"
"testing" "testing"
"time" "time"
@@ -54,11 +55,15 @@ func (s *batchUuidsProviderTestSuite) TearDownTest() {
} }
func (s *batchUuidsProviderTestSuite) GetUuidAsync(username string) <-chan *batchUuidsProviderGetUuidResult { func (s *batchUuidsProviderTestSuite) GetUuidAsync(username string) <-chan *batchUuidsProviderGetUuidResult {
return s.GetUuidAsyncWithCtx(context.Background(), username)
}
func (s *batchUuidsProviderTestSuite) GetUuidAsyncWithCtx(ctx context.Context, username string) <-chan *batchUuidsProviderGetUuidResult {
startedChan := make(chan any) startedChan := make(chan any)
c := make(chan *batchUuidsProviderGetUuidResult, 1) c := make(chan *batchUuidsProviderGetUuidResult, 1)
go func() { go func() {
close(startedChan) close(startedChan)
profile, err := s.Provider.GetUuid(username) profile, err := s.Provider.GetUuid(ctx, username)
c <- &batchUuidsProviderGetUuidResult{ c <- &batchUuidsProviderGetUuidResult{
Result: profile, Result: profile,
Error: err, Error: err,
@@ -125,6 +130,38 @@ func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesSplitByMultiple
s.Require().NotEmpty(resultChan4) s.Require().NotEmpty(resultChan4)
} }
func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesWhenOneOfContextIsDeadlined() {
var emptyResponse []string
s.MojangApi.On("UsernamesToUuids", []string{"username1", "username2", "username4"}).Once().Return(emptyResponse, nil)
ctx, cancelCtx := context.WithCancel(context.Background())
resultChan1 := s.GetUuidAsync("username1")
resultChan2 := s.GetUuidAsync("username2")
resultChan3 := s.GetUuidAsyncWithCtx(ctx, "username3")
resultChan4 := s.GetUuidAsync("username4")
cancelCtx()
time.Sleep(time.Duration(float64(awaitDelay) * 0.5))
s.Empty(resultChan1)
s.Empty(resultChan2)
s.NotEmpty(resultChan3, "canceled context must immediately release the job")
s.Empty(resultChan4)
result3 := <-resultChan3
s.Nil(result3.Result)
s.ErrorIs(result3.Error, context.Canceled)
time.Sleep(awaitDelay)
s.Require().NotEmpty(resultChan1)
s.Require().NotEmpty(resultChan2)
s.Require().NotEmpty(resultChan4)
}
func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesFireOnFull() { func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesFireOnFull() {
s.Provider.fireOnFull = true s.Provider.fireOnFull = true

View File

@@ -1,6 +1,7 @@
package mojang package mojang
import ( import (
"context"
"errors" "errors"
"regexp" "regexp"
"strings" "strings"
@@ -14,11 +15,11 @@ var InvalidUsername = errors.New("the username passed doesn't meet Mojang's requ
var allowedUsernamesRegex = regexp.MustCompile(`(?i)^[0-9a-z_]{3,16}$`) var allowedUsernamesRegex = regexp.MustCompile(`(?i)^[0-9a-z_]{3,16}$`)
type UuidsProvider interface { type UuidsProvider interface {
GetUuid(username string) (*ProfileInfo, error) GetUuid(ctx context.Context, username string) (*ProfileInfo, error)
} }
type TexturesProvider interface { type TexturesProvider interface {
GetTextures(uuid string) (*ProfileResponse, error) GetTextures(ctx context.Context, uuid string) (*ProfileResponse, error)
} }
type MojangTexturesProvider struct { type MojangTexturesProvider struct {
@@ -28,7 +29,7 @@ type MojangTexturesProvider struct {
group singleflight.Group[string, *ProfileResponse] group singleflight.Group[string, *ProfileResponse]
} }
func (p *MojangTexturesProvider) GetForUsername(username string) (*ProfileResponse, error) { func (p *MojangTexturesProvider) GetForUsername(ctx context.Context, username string) (*ProfileResponse, error) {
if !allowedUsernamesRegex.MatchString(username) { if !allowedUsernamesRegex.MatchString(username) {
return nil, InvalidUsername return nil, InvalidUsername
} }
@@ -36,7 +37,7 @@ func (p *MojangTexturesProvider) GetForUsername(username string) (*ProfileRespon
username = strings.ToLower(username) username = strings.ToLower(username)
result, err, _ := p.group.Do(username, func() (*ProfileResponse, error) { result, err, _ := p.group.Do(username, func() (*ProfileResponse, error) {
profile, err := p.UuidsProvider.GetUuid(username) profile, err := p.UuidsProvider.GetUuid(ctx, username)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -45,7 +46,7 @@ func (p *MojangTexturesProvider) GetForUsername(username string) (*ProfileRespon
return nil, nil return nil, nil
} }
return p.TexturesProvider.GetTextures(profile.Id) return p.TexturesProvider.GetTextures(ctx, profile.Id)
}) })
return result, err return result, err
@@ -54,6 +55,6 @@ func (p *MojangTexturesProvider) GetForUsername(username string) (*ProfileRespon
type NilProvider struct { type NilProvider struct {
} }
func (*NilProvider) GetForUsername(username string) (*ProfileResponse, error) { func (*NilProvider) GetForUsername(ctx context.Context, username string) (*ProfileResponse, error) {
return nil, nil return nil, nil
} }

View File

@@ -1,6 +1,7 @@
package mojang package mojang
import ( import (
"context"
"errors" "errors"
"sync" "sync"
"testing" "testing"
@@ -15,8 +16,8 @@ type mockUuidsProvider struct {
mock.Mock mock.Mock
} }
func (m *mockUuidsProvider) GetUuid(username string) (*ProfileInfo, error) { func (m *mockUuidsProvider) GetUuid(ctx context.Context, username string) (*ProfileInfo, error) {
args := m.Called(username) args := m.Called(ctx, username)
var result *ProfileInfo var result *ProfileInfo
if casted, ok := args.Get(0).(*ProfileInfo); ok { if casted, ok := args.Get(0).(*ProfileInfo); ok {
result = casted result = casted
@@ -29,8 +30,8 @@ type TexturesProviderMock struct {
mock.Mock mock.Mock
} }
func (m *TexturesProviderMock) GetTextures(uuid string) (*ProfileResponse, error) { func (m *TexturesProviderMock) GetTextures(ctx context.Context, uuid string) (*ProfileResponse, error) {
args := m.Called(uuid) args := m.Called(ctx, uuid)
var result *ProfileResponse var result *ProfileResponse
if casted, ok := args.Get(0).(*ProfileResponse); ok { if casted, ok := args.Get(0).(*ProfileResponse); ok {
result = casted result = casted
@@ -46,64 +47,64 @@ type providerTestSuite struct {
TexturesProvider *TexturesProviderMock TexturesProvider *TexturesProviderMock
} }
func (suite *providerTestSuite) SetupTest() { func (s *providerTestSuite) SetupTest() {
suite.UuidsProvider = &mockUuidsProvider{} s.UuidsProvider = &mockUuidsProvider{}
suite.TexturesProvider = &TexturesProviderMock{} s.TexturesProvider = &TexturesProviderMock{}
suite.Provider = &MojangTexturesProvider{ s.Provider = &MojangTexturesProvider{
UuidsProvider: suite.UuidsProvider, UuidsProvider: s.UuidsProvider,
TexturesProvider: suite.TexturesProvider, TexturesProvider: s.TexturesProvider,
} }
} }
func (suite *providerTestSuite) TearDownTest() { func (s *providerTestSuite) TearDownTest() {
suite.UuidsProvider.AssertExpectations(suite.T()) s.UuidsProvider.AssertExpectations(s.T())
suite.TexturesProvider.AssertExpectations(suite.T()) s.TexturesProvider.AssertExpectations(s.T())
} }
func (suite *providerTestSuite) TestGetForValidUsernameSuccessfully() { func (s *providerTestSuite) TestGetForValidUsernameSuccessfully() {
expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"} expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
expectedResult := &ProfileResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"} expectedResult := &ProfileResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
ctx := context.Background()
suite.UuidsProvider.On("GetUuid", "username").Once().Return(expectedProfile, nil) s.UuidsProvider.On("GetUuid", ctx, "username").Once().Return(expectedProfile, nil)
suite.TexturesProvider.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(expectedResult, nil) s.TexturesProvider.On("GetTextures", ctx, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(expectedResult, nil)
result, err := suite.Provider.GetForUsername("username") result, err := s.Provider.GetForUsername(ctx, "username")
suite.Assert().NoError(err) s.NoError(err)
suite.Assert().Equal(expectedResult, result) s.Same(expectedResult, result)
} }
func (suite *providerTestSuite) TestGetForUsernameWhichHasNoMojangAccount() { func (s *providerTestSuite) TestGetForUsernameWhichHasNoMojangAccount() {
suite.UuidsProvider.On("GetUuid", "username").Once().Return(nil, nil) s.UuidsProvider.On("GetUuid", mock.Anything, "username").Once().Return(nil, nil)
result, err := suite.Provider.GetForUsername("username") result, err := s.Provider.GetForUsername(context.Background(), "username")
suite.Assert().NoError(err) s.NoError(err)
suite.Assert().Nil(result) s.Nil(result)
} }
func (suite *providerTestSuite) TestGetForUsernameWhichHasMojangAccountButHasNoMojangSkin() { func (s *providerTestSuite) TestGetForUsernameWhichHasMojangAccountButHasNoMojangSkin() {
expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"} expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
suite.UuidsProvider.On("GetUuid", "username").Once().Return(expectedProfile, nil) s.UuidsProvider.On("GetUuid", mock.Anything, "username").Once().Return(expectedProfile, nil)
suite.TexturesProvider.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil, nil) s.TexturesProvider.On("GetTextures", mock.Anything, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil, nil)
result, err := suite.Provider.GetForUsername("username") result, err := s.Provider.GetForUsername(context.Background(), "username")
suite.Assert().NoError(err) s.NoError(err)
suite.Assert().Nil(result) s.Nil(result)
} }
func (suite *providerTestSuite) TestGetForTheSameUsername() { func (s *providerTestSuite) TestGetForTheSameUsernameInRow() {
expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"} expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
expectedResult := &ProfileResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"} expectedResult := &ProfileResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
awaitChan := make(chan time.Time) awaitChan := make(chan time.Time)
// If possible, then remove this .After call s.UuidsProvider.On("GetUuid", mock.Anything, "username").Once().WaitUntil(awaitChan).Return(expectedProfile, nil)
suite.UuidsProvider.On("GetUuid", "username").Once().WaitUntil(awaitChan).Return(expectedProfile, nil) s.TexturesProvider.On("GetTextures", mock.Anything, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(expectedResult, nil)
suite.TexturesProvider.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(expectedResult, nil)
results := make([]*ProfileResponse, 2) results := make([]*ProfileResponse, 2)
var wgStarted sync.WaitGroup var wgStarted sync.WaitGroup
@@ -113,7 +114,7 @@ func (suite *providerTestSuite) TestGetForTheSameUsername() {
wgDone.Add(1) wgDone.Add(1)
go func(i int) { go func(i int) {
wgStarted.Done() wgStarted.Done()
textures, _ := suite.Provider.GetForUsername("username") textures, _ := s.Provider.GetForUsername(context.Background(), "username")
results[i] = textures results[i] = textures
wgDone.Done() wgDone.Done()
}(i) }(i)
@@ -123,35 +124,48 @@ func (suite *providerTestSuite) TestGetForTheSameUsername() {
close(awaitChan) close(awaitChan)
wgDone.Wait() wgDone.Wait()
suite.Assert().Equal(expectedResult, results[0]) s.Same(expectedResult, results[0])
suite.Assert().Equal(expectedResult, results[1]) s.Same(expectedResult, results[1])
} }
func (suite *providerTestSuite) TestGetForNotAllowedMojangUsername() { func (s *providerTestSuite) TestGetForTheSameUsernameOneAfterAnother() {
result, err := suite.Provider.GetForUsername("Not allowed") expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
suite.Assert().ErrorIs(err, InvalidUsername) expectedResult := &ProfileResponse{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
suite.Assert().Nil(result)
s.UuidsProvider.On("GetUuid", mock.Anything, "username").Times(2).Return(expectedProfile, nil)
s.TexturesProvider.On("GetTextures", mock.Anything, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Times(2).Return(expectedResult, nil)
// Just ensure that providers will be called twice
_, _ = s.Provider.GetForUsername(context.Background(), "username")
time.Sleep(time.Millisecond * 20)
_, _ = s.Provider.GetForUsername(context.Background(), "username")
} }
func (suite *providerTestSuite) TestGetErrorFromUuidsProvider() { func (s *providerTestSuite) TestGetForNotAllowedMojangUsername() {
result, err := s.Provider.GetForUsername(context.Background(), "Not allowed")
s.ErrorIs(err, InvalidUsername)
s.Nil(result)
}
func (s *providerTestSuite) TestGetErrorFromUuidsProvider() {
err := errors.New("mock error") err := errors.New("mock error")
suite.UuidsProvider.On("GetUuid", "username").Once().Return(nil, err) s.UuidsProvider.On("GetUuid", mock.Anything, "username").Once().Return(nil, err)
result, resErr := suite.Provider.GetForUsername("username") result, resErr := s.Provider.GetForUsername(context.Background(), "username")
suite.Assert().Nil(result) s.Nil(result)
suite.Assert().Equal(err, resErr) s.Equal(err, resErr)
} }
func (suite *providerTestSuite) TestGetErrorFromTexturesProvider() { func (s *providerTestSuite) TestGetErrorFromTexturesProvider() {
expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"} expectedProfile := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username"}
err := errors.New("mock error") err := errors.New("mock error")
suite.UuidsProvider.On("GetUuid", "username").Once().Return(expectedProfile, nil) s.UuidsProvider.On("GetUuid", mock.Anything, "username").Once().Return(expectedProfile, nil)
suite.TexturesProvider.On("GetTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil, err) s.TexturesProvider.On("GetTextures", mock.Anything, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil, err)
result, resErr := suite.Provider.GetForUsername("username") result, resErr := s.Provider.GetForUsername(context.Background(), "username")
suite.Assert().Nil(result) s.Nil(result)
suite.Assert().Equal(err, resErr) s.Same(err, resErr)
} }
func TestProvider(t *testing.T) { func TestProvider(t *testing.T) {
@@ -160,7 +174,7 @@ func TestProvider(t *testing.T) {
func TestNilProvider_GetForUsername(t *testing.T) { func TestNilProvider_GetForUsername(t *testing.T) {
provider := &NilProvider{} provider := &NilProvider{}
result, err := provider.GetForUsername("username") result, err := provider.GetForUsername(context.Background(), "username")
require.Nil(t, result) require.Nil(t, result)
require.NoError(t, err) require.NoError(t, err)
} }

View File

@@ -1,6 +1,7 @@
package mojang package mojang
import ( import (
"context"
"sync" "sync"
"time" "time"
@@ -11,8 +12,8 @@ type MojangApiTexturesProvider struct {
MojangApiTexturesEndpoint func(uuid string, signed bool) (*ProfileResponse, error) MojangApiTexturesEndpoint func(uuid string, signed bool) (*ProfileResponse, error)
} }
func (ctx *MojangApiTexturesProvider) GetTextures(uuid string) (*ProfileResponse, error) { func (p *MojangApiTexturesProvider) GetTextures(ctx context.Context, uuid string) (*ProfileResponse, error) {
return ctx.MojangApiTexturesEndpoint(uuid, true) return p.MojangApiTexturesEndpoint(uuid, true)
} }
// Perfectly there should be an object with provider and cache implementation, // Perfectly there should be an object with provider and cache implementation,
@@ -35,14 +36,14 @@ func NewTexturesProviderWithInMemoryCache(provider TexturesProvider) *TexturesPr
return storage return storage
} }
func (s *TexturesProviderWithInMemoryCache) GetTextures(uuid string) (*ProfileResponse, error) { func (s *TexturesProviderWithInMemoryCache) GetTextures(ctx context.Context, uuid string) (*ProfileResponse, error) {
item := s.cache.Get(uuid) item := s.cache.Get(uuid)
// Don't check item.IsExpired() since Get function is already did this check // Don't check item.IsExpired() since Get function is already did this check
if item != nil { if item != nil {
return item.Value(), nil return item.Value(), nil
} }
result, err := s.provider.GetTextures(uuid) result, err := s.provider.GetTextures(ctx, uuid)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -1,6 +1,7 @@
package mojang package mojang
import ( import (
"context"
"errors" "errors"
"testing" "testing"
"time" "time"
@@ -64,7 +65,7 @@ func (s *MojangApiTexturesProviderSuite) TearDownTest() {
func (s *MojangApiTexturesProviderSuite) TestGetTextures() { func (s *MojangApiTexturesProviderSuite) TestGetTextures() {
s.MojangApi.On("UuidToTextures", "dead24f9a4fa4877b7b04c8c6c72bb46", true).Once().Return(signedTexturesResponse, nil) s.MojangApi.On("UuidToTextures", "dead24f9a4fa4877b7b04c8c6c72bb46", true).Once().Return(signedTexturesResponse, nil)
result, err := s.Provider.GetTextures("dead24f9a4fa4877b7b04c8c6c72bb46") result, err := s.Provider.GetTextures(context.Background(), "dead24f9a4fa4877b7b04c8c6c72bb46")
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(signedTexturesResponse, result) s.Require().Equal(signedTexturesResponse, result)
@@ -74,7 +75,7 @@ func (s *MojangApiTexturesProviderSuite) TestGetTexturesWithError() {
expectedError := errors.New("mock error") expectedError := errors.New("mock error")
s.MojangApi.On("UuidToTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true).Once().Return(nil, expectedError) s.MojangApi.On("UuidToTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true).Once().Return(nil, expectedError)
result, err := s.Provider.GetTextures("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") result, err := s.Provider.GetTextures(context.Background(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
s.Require().Nil(result) s.Require().Nil(result)
s.Require().Equal(expectedError, err) s.Require().Equal(expectedError, err)
@@ -101,10 +102,11 @@ func (s *TexturesProviderWithInMemoryCacheSuite) TearDownTest() {
} }
func (s *TexturesProviderWithInMemoryCacheSuite) TestGetTexturesWithSuccessfulOriginalProviderResponse() { func (s *TexturesProviderWithInMemoryCacheSuite) TestGetTexturesWithSuccessfulOriginalProviderResponse() {
s.Original.On("GetTextures", "uuid").Once().Return(signedTexturesResponse, nil) ctx := context.Background()
s.Original.On("GetTextures", ctx, "uuid").Once().Return(signedTexturesResponse, nil)
// Do the call multiple times to ensure, that there will be only one call to the Original provider // Do the call multiple times to ensure, that there will be only one call to the Original provider
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
result, err := s.Provider.GetTextures("uuid") result, err := s.Provider.GetTextures(ctx, "uuid")
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Same(signedTexturesResponse, result) s.Require().Same(signedTexturesResponse, result)
@@ -112,10 +114,10 @@ func (s *TexturesProviderWithInMemoryCacheSuite) TestGetTexturesWithSuccessfulOr
} }
func (s *TexturesProviderWithInMemoryCacheSuite) TestGetTexturesWithEmptyOriginalProviderResponse() { func (s *TexturesProviderWithInMemoryCacheSuite) TestGetTexturesWithEmptyOriginalProviderResponse() {
s.Original.On("GetTextures", "uuid").Once().Return(nil, nil) s.Original.On("GetTextures", mock.Anything, "uuid").Once().Return(nil, nil)
// Do the call multiple times to ensure, that there will be only one call to the original provider // Do the call multiple times to ensure, that there will be only one call to the original provider
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
result, err := s.Provider.GetTextures("uuid") result, err := s.Provider.GetTextures(context.Background(), "uuid")
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Nil(result) s.Require().Nil(result)
@@ -124,10 +126,10 @@ func (s *TexturesProviderWithInMemoryCacheSuite) TestGetTexturesWithEmptyOrigina
func (s *TexturesProviderWithInMemoryCacheSuite) TestGetTexturesWithErrorFromOriginalProvider() { func (s *TexturesProviderWithInMemoryCacheSuite) TestGetTexturesWithErrorFromOriginalProvider() {
expectedErr := errors.New("mock error") expectedErr := errors.New("mock error")
s.Original.On("GetTextures", "uuid").Times(5).Return(nil, expectedErr) s.Original.On("GetTextures", mock.Anything, "uuid").Times(5).Return(nil, expectedErr)
// Do the call multiple times to ensure, that the error will not be cached and there will be a request on each call // Do the call multiple times to ensure, that the error will not be cached and there will be a request on each call
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
result, err := s.Provider.GetTextures("uuid") result, err := s.Provider.GetTextures(context.Background(), "uuid")
s.Require().Same(expectedErr, err) s.Require().Same(expectedErr, err)
s.Require().Nil(result) s.Require().Nil(result)

View File

@@ -1,5 +1,7 @@
package mojang package mojang
import "context"
type MojangUuidsStorage interface { type MojangUuidsStorage interface {
// The second argument must be returned as a incoming username in case, // The second argument must be returned as a incoming username in case,
// when cached result indicates that there is no Mojang user with provided username // when cached result indicates that there is no Mojang user with provided username
@@ -13,7 +15,7 @@ type UuidsProviderWithCache struct {
Storage MojangUuidsStorage Storage MojangUuidsStorage
} }
func (p *UuidsProviderWithCache) GetUuid(username string) (*ProfileInfo, error) { func (p *UuidsProviderWithCache) GetUuid(ctx context.Context, username string) (*ProfileInfo, error) {
uuid, foundUsername, err := p.Storage.GetUuidForMojangUsername(username) uuid, foundUsername, err := p.Storage.GetUuidForMojangUsername(username)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -27,7 +29,7 @@ func (p *UuidsProviderWithCache) GetUuid(username string) (*ProfileInfo, error)
return nil, nil return nil, nil
} }
profile, err := p.Provider.GetUuid(username) profile, err := p.Provider.GetUuid(ctx, username)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -1,6 +1,7 @@
package mojang package mojang
import ( import (
"context"
"errors" "errors"
"testing" "testing"
@@ -14,8 +15,8 @@ type UuidsProviderMock struct {
mock.Mock mock.Mock
} }
func (m *UuidsProviderMock) GetUuid(username string) (*ProfileInfo, error) { func (m *UuidsProviderMock) GetUuid(ctx context.Context, username string) (*ProfileInfo, error) {
args := m.Called(username) args := m.Called(ctx, username)
var result *ProfileInfo var result *ProfileInfo
if casted, ok := args.Get(0).(*ProfileInfo); ok { if casted, ok := args.Get(0).(*ProfileInfo); ok {
result = casted result = casted
@@ -61,12 +62,14 @@ func (s *UuidsProviderWithCacheSuite) TearDownTest() {
} }
func (s *UuidsProviderWithCacheSuite) TestUncachedSuccessfully() { func (s *UuidsProviderWithCacheSuite) TestUncachedSuccessfully() {
ctx := context.Background()
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil) s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil)
s.Storage.On("StoreMojangUuid", "UserName", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil) s.Storage.On("StoreMojangUuid", "UserName", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil)
s.Original.On("GetUuid", "username").Once().Return(mockProfile, nil) s.Original.On("GetUuid", ctx, "username").Once().Return(mockProfile, nil)
result, err := s.Provider.GetUuid("username") result, err := s.Provider.GetUuid(ctx, "username")
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(mockProfile, result) s.Require().Equal(mockProfile, result)
@@ -76,9 +79,9 @@ func (s *UuidsProviderWithCacheSuite) TestUncachedNotExistsMojangUsername() {
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil) s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil)
s.Storage.On("StoreMojangUuid", "username", "").Once().Return(nil) s.Storage.On("StoreMojangUuid", "username", "").Once().Return(nil)
s.Original.On("GetUuid", "username").Once().Return(nil, nil) s.Original.On("GetUuid", mock.Anything, "username").Once().Return(nil, nil)
result, err := s.Provider.GetUuid("username") result, err := s.Provider.GetUuid(context.Background(), "username")
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Nil(result) s.Require().Nil(result)
@@ -87,7 +90,7 @@ func (s *UuidsProviderWithCacheSuite) TestUncachedNotExistsMojangUsername() {
func (s *UuidsProviderWithCacheSuite) TestKnownCachedUsername() { func (s *UuidsProviderWithCacheSuite) TestKnownCachedUsername() {
s.Storage.On("GetUuidForMojangUsername", "username").Return("mock-uuid", "UserName", nil) s.Storage.On("GetUuidForMojangUsername", "username").Return("mock-uuid", "UserName", nil)
result, err := s.Provider.GetUuid("username") result, err := s.Provider.GetUuid(context.Background(), "username")
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(result) s.Require().NotNil(result)
@@ -98,7 +101,7 @@ func (s *UuidsProviderWithCacheSuite) TestKnownCachedUsername() {
func (s *UuidsProviderWithCacheSuite) TestUnknownCachedUsername() { func (s *UuidsProviderWithCacheSuite) TestUnknownCachedUsername() {
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "UserName", nil) s.Storage.On("GetUuidForMojangUsername", "username").Return("", "UserName", nil)
result, err := s.Provider.GetUuid("username") result, err := s.Provider.GetUuid(context.Background(), "username")
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Nil(result) s.Require().Nil(result)
@@ -108,7 +111,7 @@ func (s *UuidsProviderWithCacheSuite) TestErrorDuringCacheQuery() {
expectedError := errors.New("mock error") expectedError := errors.New("mock error")
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", expectedError) s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", expectedError)
result, err := s.Provider.GetUuid("username") result, err := s.Provider.GetUuid(context.Background(), "username")
s.Require().Same(expectedError, err) s.Require().Same(expectedError, err)
s.Require().Nil(result) s.Require().Nil(result)
@@ -118,9 +121,9 @@ func (s *UuidsProviderWithCacheSuite) TestErrorFromOriginalProvider() {
expectedError := errors.New("mock error") expectedError := errors.New("mock error")
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil) s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil)
s.Original.On("GetUuid", "username").Once().Return(nil, expectedError) s.Original.On("GetUuid", mock.Anything, "username").Once().Return(nil, expectedError)
result, err := s.Provider.GetUuid("username") result, err := s.Provider.GetUuid(context.Background(), "username")
s.Require().Same(expectedError, err) s.Require().Same(expectedError, err)
s.Require().Nil(result) s.Require().Nil(result)

View File

@@ -1,6 +1,7 @@
package profiles package profiles
import ( import (
"context"
"errors" "errors"
"ely.by/chrly/internal/db" "ely.by/chrly/internal/db"
@@ -12,7 +13,7 @@ type ProfilesFinder interface {
} }
type MojangProfilesProvider interface { type MojangProfilesProvider interface {
GetForUsername(username string) (*mojang.ProfileResponse, error) GetForUsername(ctx context.Context, username string) (*mojang.ProfileResponse, error)
} }
type Provider struct { type Provider struct {
@@ -20,7 +21,7 @@ type Provider struct {
MojangProfilesProvider MojangProfilesProvider
} }
func (p *Provider) FindProfileByUsername(username string, allowProxy bool) (*db.Profile, error) { func (p *Provider) FindProfileByUsername(ctx context.Context, username string, allowProxy bool) (*db.Profile, error) {
profile, err := p.ProfilesFinder.FindProfileByUsername(username) profile, err := p.ProfilesFinder.FindProfileByUsername(username)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -31,7 +32,7 @@ func (p *Provider) FindProfileByUsername(username string, allowProxy bool) (*db.
} }
if allowProxy { if allowProxy {
mojangProfile, err := p.MojangProfilesProvider.GetForUsername(username) mojangProfile, err := p.MojangProfilesProvider.GetForUsername(ctx, username)
// If we at least know something about the user, // If we at least know something about the user,
// then we can ignore an error and return profile without textures // then we can ignore an error and return profile without textures
if err != nil && profile != nil { if err != nil && profile != nil {

View File

@@ -1,6 +1,7 @@
package profiles package profiles
import ( import (
"context"
"errors" "errors"
"testing" "testing"
"time" "time"
@@ -31,8 +32,8 @@ type MojangProfilesProviderMock struct {
mock.Mock mock.Mock
} }
func (m *MojangProfilesProviderMock) GetForUsername(username string) (*mojang.ProfileResponse, error) { func (m *MojangProfilesProviderMock) GetForUsername(ctx context.Context, username string) (*mojang.ProfileResponse, error) {
args := m.Called(username) args := m.Called(ctx, username)
var result *mojang.ProfileResponse var result *mojang.ProfileResponse
if casted, ok := args.Get(0).(*mojang.ProfileResponse); ok { if casted, ok := args.Get(0).(*mojang.ProfileResponse); ok {
result = casted result = casted
@@ -46,21 +47,21 @@ type CombinedProfilesProviderSuite struct {
Provider *Provider Provider *Provider
ProfilesRepository *ProfilesFinderMock ProfilesFinder *ProfilesFinderMock
MojangProfilesProvider *MojangProfilesProviderMock MojangProfilesProvider *MojangProfilesProviderMock
} }
func (t *CombinedProfilesProviderSuite) SetupSubTest() { func (t *CombinedProfilesProviderSuite) SetupSubTest() {
t.ProfilesRepository = &ProfilesFinderMock{} t.ProfilesFinder = &ProfilesFinderMock{}
t.MojangProfilesProvider = &MojangProfilesProviderMock{} t.MojangProfilesProvider = &MojangProfilesProviderMock{}
t.Provider = &Provider{ t.Provider = &Provider{
ProfilesFinder: t.ProfilesRepository, ProfilesFinder: t.ProfilesFinder,
MojangProfilesProvider: t.MojangProfilesProvider, MojangProfilesProvider: t.MojangProfilesProvider,
} }
} }
func (t *CombinedProfilesProviderSuite) TearDownSubTest() { func (t *CombinedProfilesProviderSuite) TearDownSubTest() {
t.ProfilesRepository.AssertExpectations(t.T()) t.ProfilesFinder.AssertExpectations(t.T())
t.MojangProfilesProvider.AssertExpectations(t.T()) t.MojangProfilesProvider.AssertExpectations(t.T())
} }
@@ -71,9 +72,9 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() {
Username: "Mock", Username: "Mock",
SkinUrl: "https://example.com/skin.png", SkinUrl: "https://example.com/skin.png",
} }
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(profile, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true)
t.NoError(err) t.NoError(err)
t.Same(profile, foundProfile) t.Same(profile, foundProfile)
}) })
@@ -84,9 +85,9 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() {
Username: "Mock", Username: "Mock",
CapeUrl: "https://example.com/cape.png", CapeUrl: "https://example.com/cape.png",
} }
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(profile, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true)
t.NoError(err) t.NoError(err)
t.Same(profile, foundProfile) t.Same(profile, foundProfile)
}) })
@@ -96,26 +97,26 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() {
Uuid: "mock-uuid", Uuid: "mock-uuid",
Username: "Mock", Username: "Mock",
} }
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(profile, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", false) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", false)
t.NoError(err) t.NoError(err)
t.Same(profile, foundProfile) t.Same(profile, foundProfile)
}) })
t.Run("not exists profile (no proxy)", func() { t.Run("not exists profile (no proxy)", func() {
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(nil, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", false) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", false)
t.NoError(err) t.NoError(err)
t.Nil(foundProfile) t.Nil(foundProfile)
}) })
t.Run("handle error from profiles repository", func() { t.Run("handle error from profiles repository", func() {
expectedError := errors.New("mock error") expectedError := errors.New("mock error")
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(nil, expectedError) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, expectedError)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", false) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", false)
t.Same(expectedError, err) t.Same(expectedError, err)
t.Nil(foundProfile) t.Nil(foundProfile)
}) })
@@ -126,10 +127,11 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() {
Username: "Mock", Username: "Mock",
} }
mojangProfile := createMojangProfile(true, true) mojangProfile := createMojangProfile(true, true)
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(profile, nil) ctx := context.Background()
t.MojangProfilesProvider.On("GetForUsername", "Mock").Return(mojangProfile, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil)
t.MojangProfilesProvider.On("GetForUsername", ctx, "Mock").Return(mojangProfile, nil)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(ctx, "Mock", true)
t.NoError(err) t.NoError(err)
t.Equal(&db.Profile{ t.Equal(&db.Profile{
Uuid: "mock-mojang-uuid", Uuid: "mock-mojang-uuid",
@@ -144,10 +146,10 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() {
t.Run("not exists profile (with proxy)", func() { t.Run("not exists profile (with proxy)", func() {
mojangProfile := createMojangProfile(true, true) mojangProfile := createMojangProfile(true, true)
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(nil, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil)
t.MojangProfilesProvider.On("GetForUsername", "Mock").Return(mojangProfile, nil) t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(mojangProfile, nil)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true)
t.NoError(err) t.NoError(err)
t.Equal(&db.Profile{ t.Equal(&db.Profile{
Uuid: "mock-mojang-uuid", Uuid: "mock-mojang-uuid",
@@ -165,29 +167,29 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() {
Uuid: "mock-uuid", Uuid: "mock-uuid",
Username: "Mock", Username: "Mock",
} }
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(profile, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil)
t.MojangProfilesProvider.On("GetForUsername", "Mock").Return(nil, errors.New("mock error")) t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(nil, errors.New("mock error"))
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true)
t.NoError(err) t.NoError(err)
t.Same(profile, foundProfile) t.Same(profile, foundProfile)
}) })
t.Run("should not return an error when passed the invalid username", func() { t.Run("should not return an error when passed the invalid username", func() {
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(nil, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil)
t.MojangProfilesProvider.On("GetForUsername", "Mock").Return(nil, mojang.InvalidUsername) t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(nil, mojang.InvalidUsername)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true)
t.NoError(err) t.NoError(err)
t.Nil(foundProfile) t.Nil(foundProfile)
}) })
t.Run("should return an error from mojang provider", func() { t.Run("should return an error from mojang provider", func() {
expectedError := errors.New("mock error") expectedError := errors.New("mock error")
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(nil, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil)
t.MojangProfilesProvider.On("GetForUsername", "Mock").Return(nil, expectedError) t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(nil, expectedError)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true)
t.Same(expectedError, err) t.Same(expectedError, err)
t.Nil(foundProfile) t.Nil(foundProfile)
}) })
@@ -202,10 +204,10 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() {
}, },
}, },
} }
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(nil, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil)
t.MojangProfilesProvider.On("GetForUsername", "Mock").Return(mojangProfile, nil) t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(mojangProfile, nil)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true)
t.ErrorContains(err, "illegal base64 data") t.ErrorContains(err, "illegal base64 data")
t.Nil(foundProfile) t.Nil(foundProfile)
}) })
@@ -216,10 +218,10 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() {
Name: "mOcK", Name: "mOcK",
Props: []*mojang.Property{}, Props: []*mojang.Property{},
} }
t.ProfilesRepository.On("FindProfileByUsername", "Mock").Return(nil, nil) t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil)
t.MojangProfilesProvider.On("GetForUsername", "Mock").Return(mojangProfile, nil) t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(mojangProfile, nil)
foundProfile, err := t.Provider.FindProfileByUsername("Mock", true) foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true)
t.NoError(err) t.NoError(err)
t.Equal(&db.Profile{ t.Equal(&db.Profile{
Uuid: "mock-mojang-uuid", Uuid: "mock-mojang-uuid",