#1: Tests for http layer are restored

This commit is contained in:
ErickSkrauch 2019-04-28 00:43:22 +03:00
parent f7cdab243f
commit 2c7a1625f3
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
9 changed files with 1051 additions and 808 deletions

View File

@ -17,152 +17,8 @@ import (
testify "github.com/stretchr/testify/assert" testify "github.com/stretchr/testify/assert"
) )
func TestConfig_PostSkin_Valid(t *testing.T) { func TestConfig_PostSkin(t *testing.T) {
assert := testify.New(t) t.Run("Upload new identity with textures info", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
resultModel := createSkinModel("mock_user", false)
resultModel.SkinId = 5
resultModel.Hash = "94a457d92a61460cb9cb5d6f29732d2a"
resultModel.Url = "http://ely.by/minecraft/skins/default.png"
resultModel.MojangTextures = ""
resultModel.MojangSignature = ""
mocks.Auth.EXPECT().Check(gomock.Any()).Return(nil)
mocks.Skins.EXPECT().FindByUserId(1).Return(createSkinModel("mock_user", false), nil)
mocks.Skins.EXPECT().Save(resultModel).Return(nil)
mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1))
mocks.Log.EXPECT().IncCounter("authentication.success", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.request", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.success", int64(1))
form := url.Values{
"identityId": {"1"},
"username": {"mock_user"},
"uuid": {"0f657aa8-bfbe-415d-b700-5750090d3af3"},
"skinId": {"5"},
"hash": {"94a457d92a61460cb9cb5d6f29732d2a"},
"is1_8": {"0"},
"isSlim": {"0"},
"url": {"http://ely.by/minecraft/skins/default.png"},
}
req := httptest.NewRequest("POST", "http://skinsystem.ely.by/api/skins", bytes.NewBufferString(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
defer resp.Body.Close()
assert.Equal(201, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response)
}
func TestConfig_PostSkin_ChangedIdentityId(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
resultModel := createSkinModel("mock_user", false)
resultModel.UserId = 2
resultModel.SkinId = 5
resultModel.Hash = "94a457d92a61460cb9cb5d6f29732d2a"
resultModel.Url = "http://ely.by/minecraft/skins/default.png"
resultModel.MojangTextures = ""
resultModel.MojangSignature = ""
form := url.Values{
"identityId": {"2"},
"username": {"mock_user"},
"uuid": {"0f657aa8-bfbe-415d-b700-5750090d3af3"},
"skinId": {"5"},
"hash": {"94a457d92a61460cb9cb5d6f29732d2a"},
"is1_8": {"0"},
"isSlim": {"0"},
"url": {"http://ely.by/minecraft/skins/default.png"},
}
req := httptest.NewRequest("POST", "http://skinsystem.ely.by/api/skins", bytes.NewBufferString(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
mocks.Auth.EXPECT().Check(gomock.Any()).Return(nil)
mocks.Skins.EXPECT().FindByUserId(2).Return(nil, &db.SkinNotFoundError{"unknown"})
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
mocks.Skins.EXPECT().RemoveByUsername("mock_user").Return(nil)
mocks.Skins.EXPECT().Save(resultModel).Return(nil)
mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1))
mocks.Log.EXPECT().IncCounter("authentication.success", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.request", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.success", int64(1))
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
defer resp.Body.Close()
assert.Equal(201, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response)
}
func TestConfig_PostSkin_ChangedUsername(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
resultModel := createSkinModel("changed_username", false)
resultModel.SkinId = 5
resultModel.Hash = "94a457d92a61460cb9cb5d6f29732d2a"
resultModel.Url = "http://ely.by/minecraft/skins/default.png"
resultModel.MojangTextures = ""
resultModel.MojangSignature = ""
form := url.Values{
"identityId": {"1"},
"username": {"changed_username"},
"uuid": {"0f657aa8-bfbe-415d-b700-5750090d3af3"},
"skinId": {"5"},
"hash": {"94a457d92a61460cb9cb5d6f29732d2a"},
"is1_8": {"0"},
"isSlim": {"0"},
"url": {"http://ely.by/minecraft/skins/default.png"},
}
req := httptest.NewRequest("POST", "http://skinsystem.ely.by/api/skins", bytes.NewBufferString(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
mocks.Auth.EXPECT().Check(gomock.Any()).Return(nil)
mocks.Skins.EXPECT().FindByUserId(1).Return(createSkinModel("mock_user", false), nil)
mocks.Skins.EXPECT().RemoveByUserId(1).Return(nil)
mocks.Skins.EXPECT().Save(resultModel).Return(nil)
mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1))
mocks.Log.EXPECT().IncCounter("authentication.success", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.request", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.success", int64(1))
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
defer resp.Body.Close()
assert.Equal(201, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response)
}
func TestConfig_PostSkin_CompletelyNewIdentity(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -193,8 +49,8 @@ func TestConfig_PostSkin_CompletelyNewIdentity(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
mocks.Auth.EXPECT().Check(gomock.Any()).Return(nil) mocks.Auth.EXPECT().Check(gomock.Any()).Return(nil)
mocks.Skins.EXPECT().FindByUserId(1).Return(nil, &db.SkinNotFoundError{"unknown"}) mocks.Skins.EXPECT().FindByUserId(1).Return(nil, &db.SkinNotFoundError{Who: "unknown"})
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(nil, &db.SkinNotFoundError{"mock_user"}) mocks.Skins.EXPECT().FindByUsername("mock_user").Return(nil, &db.SkinNotFoundError{Who: "mock_user"})
mocks.Skins.EXPECT().Save(resultModel).Return(nil) mocks.Skins.EXPECT().Save(resultModel).Return(nil)
mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1)) mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1))
mocks.Log.EXPECT().IncCounter("authentication.success", int64(1)) mocks.Log.EXPECT().IncCounter("authentication.success", int64(1))
@ -208,9 +64,9 @@ func TestConfig_PostSkin_CompletelyNewIdentity(t *testing.T) {
assert.Equal(201, resp.StatusCode) assert.Equal(201, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body) response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response) assert.Empty(response)
} })
func TestConfig_PostSkin_UploadSkin(t *testing.T) { t.Run("Upload new identity with skin file", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -257,9 +113,154 @@ func TestConfig_PostSkin_UploadSkin(t *testing.T) {
] ]
} }
}`, string(response)) }`, string(response))
} })
func TestConfig_PostSkin_RequiredFields(t *testing.T) { t.Run("Keep the same identityId, uuid and username, but change textures information", func(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
resultModel := createSkinModel("mock_user", false)
resultModel.SkinId = 5
resultModel.Hash = "94a457d92a61460cb9cb5d6f29732d2a"
resultModel.Url = "http://textures-server.com/skin.png"
resultModel.MojangTextures = ""
resultModel.MojangSignature = ""
mocks.Auth.EXPECT().Check(gomock.Any()).Return(nil)
mocks.Skins.EXPECT().FindByUserId(1).Return(createSkinModel("mock_user", false), nil)
mocks.Skins.EXPECT().Save(resultModel).Return(nil)
mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1))
mocks.Log.EXPECT().IncCounter("authentication.success", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.request", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.success", int64(1))
form := url.Values{
"identityId": {"1"},
"username": {"mock_user"},
"uuid": {"0f657aa8-bfbe-415d-b700-5750090d3af3"},
"skinId": {"5"},
"hash": {"94a457d92a61460cb9cb5d6f29732d2a"},
"is1_8": {"0"},
"isSlim": {"0"},
"url": {"http://textures-server.com/skin.png"},
}
req := httptest.NewRequest("POST", "http://chrly/api/skins", bytes.NewBufferString(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
defer resp.Body.Close()
assert.Equal(201, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response)
})
t.Run("Keep the same uuid and username, but change identityId", func(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
resultModel := createSkinModel("mock_user", false)
resultModel.UserId = 2
resultModel.SkinId = 5
resultModel.Hash = "94a457d92a61460cb9cb5d6f29732d2a"
resultModel.Url = "http://ely.by/minecraft/skins/default.png"
resultModel.MojangTextures = ""
resultModel.MojangSignature = ""
form := url.Values{
"identityId": {"2"},
"username": {"mock_user"},
"uuid": {"0f657aa8-bfbe-415d-b700-5750090d3af3"},
"skinId": {"5"},
"hash": {"94a457d92a61460cb9cb5d6f29732d2a"},
"is1_8": {"0"},
"isSlim": {"0"},
"url": {"http://ely.by/minecraft/skins/default.png"},
}
req := httptest.NewRequest("POST", "http://skinsystem.ely.by/api/skins", bytes.NewBufferString(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
mocks.Auth.EXPECT().Check(gomock.Any()).Return(nil)
mocks.Skins.EXPECT().FindByUserId(2).Return(nil, &db.SkinNotFoundError{Who: "unknown"})
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
mocks.Skins.EXPECT().RemoveByUsername("mock_user").Return(nil)
mocks.Skins.EXPECT().Save(resultModel).Return(nil)
mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1))
mocks.Log.EXPECT().IncCounter("authentication.success", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.request", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.success", int64(1))
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
defer resp.Body.Close()
assert.Equal(201, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response)
})
t.Run("Keep the same identityId and uuid, but change username", func(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
resultModel := createSkinModel("changed_username", false)
resultModel.SkinId = 5
resultModel.Hash = "94a457d92a61460cb9cb5d6f29732d2a"
resultModel.Url = "http://ely.by/minecraft/skins/default.png"
resultModel.MojangTextures = ""
resultModel.MojangSignature = ""
form := url.Values{
"identityId": {"1"},
"username": {"changed_username"},
"uuid": {"0f657aa8-bfbe-415d-b700-5750090d3af3"},
"skinId": {"5"},
"hash": {"94a457d92a61460cb9cb5d6f29732d2a"},
"is1_8": {"0"},
"isSlim": {"0"},
"url": {"http://ely.by/minecraft/skins/default.png"},
}
req := httptest.NewRequest("POST", "http://skinsystem.ely.by/api/skins", bytes.NewBufferString(form.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
mocks.Auth.EXPECT().Check(gomock.Any()).Return(nil)
mocks.Skins.EXPECT().FindByUserId(1).Return(createSkinModel("mock_user", false), nil)
mocks.Skins.EXPECT().RemoveByUserId(1).Return(nil)
mocks.Skins.EXPECT().Save(resultModel).Return(nil)
mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1))
mocks.Log.EXPECT().IncCounter("authentication.success", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.request", int64(1))
mocks.Log.EXPECT().IncCounter("api.skins.post.success", int64(1))
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
defer resp.Body.Close()
assert.Equal(201, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response)
})
t.Run("Get errors about required fields", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -317,9 +318,9 @@ func TestConfig_PostSkin_RequiredFields(t *testing.T) {
] ]
} }
}`, string(response)) }`, string(response))
} })
func TestConfig_PostSkin_Unauthorized(t *testing.T) { t.Run("Perform request without authorization", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -344,9 +345,11 @@ func TestConfig_PostSkin_Unauthorized(t *testing.T) {
assert.JSONEq(`{ assert.JSONEq(`{
"error": "Cannot parse passed JWT token" "error": "Cannot parse passed JWT token"
}`, string(response)) }`, string(response))
})
} }
func TestConfig_DeleteSkinByUserId_Success(t *testing.T) { func TestConfig_DeleteSkinByUserId(t *testing.T) {
t.Run("Delete skin by its identity id", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -372,9 +375,9 @@ func TestConfig_DeleteSkinByUserId_Success(t *testing.T) {
assert.Equal(204, resp.StatusCode) assert.Equal(204, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body) response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response) assert.Empty(response)
} })
func TestConfig_DeleteSkinByUserId_NotFound(t *testing.T) { t.Run("Try to remove not exists identity id", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -401,9 +404,11 @@ func TestConfig_DeleteSkinByUserId_NotFound(t *testing.T) {
assert.JSONEq(`[ assert.JSONEq(`[
"Cannot find record for requested user id" "Cannot find record for requested user id"
]`, string(response)) ]`, string(response))
})
} }
func TestConfig_DeleteSkinByUsername_Success(t *testing.T) { func TestConfig_DeleteSkinByUsername(t *testing.T) {
t.Run("Delete skin by its identity username", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -429,9 +434,9 @@ func TestConfig_DeleteSkinByUsername_Success(t *testing.T) {
assert.Equal(204, resp.StatusCode) assert.Equal(204, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body) response, _ := ioutil.ReadAll(resp.Body)
assert.Empty(response) assert.Empty(response)
} })
func TestConfig_DeleteSkinByUsername_NotFound(t *testing.T) { t.Run("Try to remove not exists identity username", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -458,9 +463,11 @@ func TestConfig_DeleteSkinByUsername_NotFound(t *testing.T) {
assert.JSONEq(`[ assert.JSONEq(`[
"Cannot find record for requested username" "Cannot find record for requested username"
]`, string(response)) ]`, string(response))
})
} }
func TestConfig_Authenticate_SignatureKeyNotSet(t *testing.T) { func TestConfig_Authenticate(t *testing.T) {
t.Run("Test behavior when signing key is not set", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -471,7 +478,7 @@ func TestConfig_Authenticate_SignatureKeyNotSet(t *testing.T) {
req := httptest.NewRequest("POST", "http://localhost", nil) req := httptest.NewRequest("POST", "http://localhost", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
mocks.Auth.EXPECT().Check(gomock.Any()).Return(&auth.Unauthorized{"signing key not available"}) mocks.Auth.EXPECT().Check(gomock.Any()).Return(&auth.Unauthorized{Reason: "signing key not available"})
mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1)) mocks.Log.EXPECT().IncCounter("authentication.challenge", int64(1))
mocks.Log.EXPECT().IncCounter("authentication.failed", int64(1)) mocks.Log.EXPECT().IncCounter("authentication.failed", int64(1))
@ -485,6 +492,7 @@ func TestConfig_Authenticate_SignatureKeyNotSet(t *testing.T) {
assert.JSONEq(`{ assert.JSONEq(`{
"error": "signing key not available" "error": "signing key not available"
}`, string(response)) }`, string(response))
})
} }
// base64 https://github.com/mathiasbynens/small/blob/0ca3c51/png-transparent.png // base64 https://github.com/mathiasbynens/small/blob/0ca3c51/png-transparent.png

View File

@ -5,6 +5,7 @@ import (
"image" "image"
"image/png" "image/png"
"io/ioutil" "io/ioutil"
"net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -15,7 +16,58 @@ import (
"github.com/elyby/chrly/model" "github.com/elyby/chrly/model"
) )
type capesTestCase struct {
Name string
RequestUrl string
ExpectedLogKey string
ExistsInLocalStorage bool
ExistsInMojang bool
HasCapeInMojangResp bool
AssertResponse func(assert *testify.Assertions, resp *http.Response)
}
var capesTestCases = []*capesTestCase{
{
Name: "Obtain cape for known username",
ExistsInLocalStorage: true,
AssertResponse: func(assert *testify.Assertions, resp *http.Response) {
assert.Equal(200, resp.StatusCode)
responseData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(createCape(), responseData)
assert.Equal("image/png", resp.Header.Get("Content-Type"))
},
},
{
Name: "Obtain cape for unknown username that exists in Mojang and has a cape",
ExistsInLocalStorage: false,
ExistsInMojang: true,
HasCapeInMojangResp: true,
AssertResponse: func(assert *testify.Assertions, resp *http.Response) {
assert.Equal(301, resp.StatusCode)
assert.Equal("http://mojang/cape.png", resp.Header.Get("Location"))
},
},
{
Name: "Obtain cape for unknown username that exists in Mojang, but don't has a cape",
ExistsInLocalStorage: false,
ExistsInMojang: true,
HasCapeInMojangResp: false,
AssertResponse: func(assert *testify.Assertions, resp *http.Response) {
assert.Equal(404, resp.StatusCode)
},
},
{
Name: "Obtain cape for unknown username that doesn't exists in Mojang",
ExistsInLocalStorage: false,
ExistsInMojang: false,
AssertResponse: func(assert *testify.Assertions, resp *http.Response) {
assert.Equal(404, resp.StatusCode)
},
},
}
func TestConfig_Cape(t *testing.T) { func TestConfig_Cape(t *testing.T) {
performTest := func(t *testing.T, testCase *capesTestCase) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -23,115 +75,88 @@ func TestConfig_Cape(t *testing.T) {
config, mocks := setupMocks(ctrl) config, mocks := setupMocks(ctrl)
cape := createCape() mocks.Log.EXPECT().IncCounter(testCase.ExpectedLogKey, int64(1))
if testCase.ExistsInLocalStorage {
mocks.Capes.EXPECT().FindByUsername("mocked_username").Return(&model.Cape{ mocks.Capes.EXPECT().FindByUsername("mock_username").Return(&model.Cape{
File: bytes.NewReader(cape), File: bytes.NewReader(createCape()),
}, nil) }, nil)
mocks.Log.EXPECT().IncCounter("capes.request", int64(1)) } else {
mocks.Capes.EXPECT().FindByUsername("mock_username").Return(nil, &db.CapeNotFoundError{Who: "mock_username"})
}
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/cloaks/mocked_username", nil) if testCase.ExistsInMojang {
textures := createTexturesResponse(false, testCase.HasCapeInMojangResp)
mocks.Queue.On("GetTexturesForUsername", "mock_username").Return(textures)
} else {
mocks.Queue.On("GetTexturesForUsername", "mock_username").Return(nil)
}
req := httptest.NewRequest("GET", testCase.RequestUrl, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req) config.CreateHandler().ServeHTTP(w, req)
resp := w.Result() resp := w.Result()
assert.Equal(200, resp.StatusCode) testCase.AssertResponse(assert, resp)
responseData, _ := ioutil.ReadAll(resp.Body) }
assert.Equal(cape, responseData)
assert.Equal("image/png", resp.Header.Get("Content-Type"))
}
func TestConfig_Cape2(t *testing.T) { t.Run("Normal API", func(t *testing.T) {
for _, testCase := range capesTestCases {
testCase.RequestUrl = "http://chrly/cloaks/mock_username"
testCase.ExpectedLogKey = "capes.request"
t.Run(testCase.Name, func(t *testing.T) {
performTest(t, testCase)
})
}
})
t.Run("GET fallback API", func(t *testing.T) {
for _, testCase := range capesTestCases {
testCase.RequestUrl = "http://chrly/cloaks?name=mock_username"
testCase.ExpectedLogKey = "capes.get_request"
t.Run(testCase.Name, func(t *testing.T) {
performTest(t, testCase)
})
}
t.Run("Should trim trailing slash", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) req := httptest.NewRequest("GET", "http://chrly/cloaks/?name=notch", nil)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
mocks.Capes.EXPECT().FindByUsername("notch").Return(nil, &db.CapeNotFoundError{"notch"})
mocks.Log.EXPECT().IncCounter("capes.request", int64(1))
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/cloaks/notch", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(301, resp.StatusCode)
assert.Equal("http://skins.minecraft.net/MinecraftCloaks/notch.png", resp.Header.Get("Location"))
}
func TestConfig_CapeGET(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
cape := createCape()
mocks.Capes.EXPECT().FindByUsername("mocked_username").Return(&model.Cape{
File: bytes.NewReader(cape),
}, nil)
mocks.Log.EXPECT().IncCounter("capes.request", int64(1)).Times(0)
mocks.Log.EXPECT().IncCounter("capes.get_request", int64(1))
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/cloaks?name=mocked_username", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(200, resp.StatusCode)
responseData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(cape, responseData)
assert.Equal("image/png", resp.Header.Get("Content-Type"))
}
func TestConfig_CapeGET2(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
mocks.Capes.EXPECT().FindByUsername("notch").Return(nil, &db.CapeNotFoundError{"notch"})
mocks.Log.EXPECT().IncCounter("capes.request", int64(1)).Times(0)
mocks.Log.EXPECT().IncCounter("capes.get_request", int64(1))
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/cloaks?name=notch", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(301, resp.StatusCode)
assert.Equal("http://skins.minecraft.net/MinecraftCloaks/notch.png", resp.Header.Get("Location"))
}
func TestConfig_CapeGET3(t *testing.T) {
assert := testify.New(t)
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/cloaks/?name=notch", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
(&Config{}).CreateHandler().ServeHTTP(w, req) (&Config{}).CreateHandler().ServeHTTP(w, req)
resp := w.Result() resp := w.Result()
assert.Equal(301, resp.StatusCode) assert.Equal(301, resp.StatusCode)
assert.Equal("http://skinsystem.ely.by/cloaks?name=notch", resp.Header.Get("Location")) assert.Equal("http://chrly/cloaks?name=notch", resp.Header.Get("Location"))
})
t.Run("Return error when name is not provided", func(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
mocks.Log.EXPECT().IncCounter("capes.get_request", int64(1))
req := httptest.NewRequest("GET", "http://chrly/cloaks", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(400, resp.StatusCode)
})
})
} }
// Cape md5: 424ff79dce9940af89c28ad80de8aaad // Cape md5: 424ff79dce9940af89c28ad80de8aaad
func createCape() []byte { func createCape() []byte {
img := image.NewAlpha(image.Rect(0, 0, 64, 32)) img := image.NewAlpha(image.Rect(0, 0, 64, 32))
writer := &bytes.Buffer{} writer := &bytes.Buffer{}
png.Encode(writer, img) _ = png.Encode(writer, img)
pngBytes, _ := ioutil.ReadAll(writer) pngBytes, _ := ioutil.ReadAll(writer)
return pngBytes return pngBytes

View File

@ -2,7 +2,11 @@ package http
import ( import (
"testing" "testing"
"time"
"github.com/elyby/chrly/api/mojang"
"github.com/elyby/chrly/tests"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
testify "github.com/stretchr/testify/assert" testify "github.com/stretchr/testify/assert"
@ -19,6 +23,7 @@ func TestParseUsername(t *testing.T) {
type mocks struct { type mocks struct {
Skins *mock_interfaces.MockSkinsRepository Skins *mock_interfaces.MockSkinsRepository
Capes *mock_interfaces.MockCapesRepository Capes *mock_interfaces.MockCapesRepository
Queue *tests.MojangTexturesQueueMock
Auth *mock_interfaces.MockAuthChecker Auth *mock_interfaces.MockAuthChecker
Log *mock_wd.MockWatchdog Log *mock_wd.MockWatchdog
} }
@ -31,16 +36,54 @@ func setupMocks(ctrl *gomock.Controller) (
capesRepo := mock_interfaces.NewMockCapesRepository(ctrl) capesRepo := mock_interfaces.NewMockCapesRepository(ctrl)
authChecker := mock_interfaces.NewMockAuthChecker(ctrl) authChecker := mock_interfaces.NewMockAuthChecker(ctrl)
wd := mock_wd.NewMockWatchdog(ctrl) wd := mock_wd.NewMockWatchdog(ctrl)
texturesQueue := &tests.MojangTexturesQueueMock{}
return &Config{ return &Config{
SkinsRepo: skinsRepo, SkinsRepo: skinsRepo,
CapesRepo: capesRepo, CapesRepo: capesRepo,
Auth: authChecker, Auth: authChecker,
MojangTexturesQueue: texturesQueue,
Logger: wd, Logger: wd,
}, &mocks{ }, &mocks{
Skins: skinsRepo, Skins: skinsRepo,
Capes: capesRepo, Capes: capesRepo,
Auth: authChecker, Auth: authChecker,
Queue: texturesQueue,
Log: wd, Log: wd,
} }
} }
func createTexturesResponse(includeSkin bool, includeCape bool) *mojang.SignedTexturesResponse {
timeZone, _ := time.LoadLocation("Europe/Minsk")
textures := &mojang.TexturesProp{
Timestamp: time.Date(2019, 4, 27, 23, 56, 12, 0, timeZone).Unix(),
ProfileID: "00000000000000000000000000000000",
ProfileName: "mock_user",
Textures: &mojang.TexturesResponse{},
}
if includeSkin {
textures.Textures.Skin = &mojang.SkinTexturesResponse{
Url: "http://mojang/skin.png",
}
}
if includeCape {
textures.Textures.Cape = &mojang.CapeTexturesResponse{
Url: "http://mojang/cape.png",
}
}
response := &mojang.SignedTexturesResponse{
Id: "00000000000000000000000000000000",
Name: "mock_user",
Props: []*mojang.Property{
{
Name: "textures",
Value: mojang.EncodeTextures(textures),
},
},
}
return response
}

View File

@ -27,10 +27,6 @@ func (cfg *Config) SignedTextures(response http.ResponseWriter, request *http.Re
Signature: rec.MojangSignature, Signature: rec.MojangSignature,
Value: rec.MojangTextures, Value: rec.MojangTextures,
}, },
{
Name: "chrly",
Value: "how do you tame a horse in Minecraft?",
},
}, },
} }
} else if request.URL.Query().Get("proxy") != "" { } else if request.URL.Query().Get("proxy") != "" {
@ -42,6 +38,11 @@ func (cfg *Config) SignedTextures(response http.ResponseWriter, request *http.Re
return return
} }
responseData.Props = append(responseData.Props, &mojang.Property{
Name: "chrly",
Value: "how do you tame a horse in Minecraft?",
})
responseJson, _ := json.Marshal(responseData) responseJson, _ := json.Marshal(responseData)
response.Header().Set("Content-Type", "application/json") response.Header().Set("Content-Type", "application/json")
response.Write(responseJson) response.Write(responseJson)

View File

@ -12,6 +12,7 @@ import (
) )
func TestConfig_SignedTextures(t *testing.T) { func TestConfig_SignedTextures(t *testing.T) {
t.Run("Obtain signed textures for exists user", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -19,10 +20,10 @@ func TestConfig_SignedTextures(t *testing.T) {
config, mocks := setupMocks(ctrl) config, mocks := setupMocks(ctrl)
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
mocks.Log.EXPECT().IncCounter("signed_textures.request", int64(1)) mocks.Log.EXPECT().IncCounter("signed_textures.request", int64(1))
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/textures/signed/mock_user", nil) req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_user", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req) config.CreateHandler().ServeHTTP(w, req)
@ -46,9 +47,9 @@ func TestConfig_SignedTextures(t *testing.T) {
} }
] ]
}`, string(response)) }`, string(response))
} })
func TestConfig_SignedTextures2(t *testing.T) { t.Run("Obtain signed textures for not exists user", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -56,10 +57,11 @@ func TestConfig_SignedTextures2(t *testing.T) {
config, mocks := setupMocks(ctrl) config, mocks := setupMocks(ctrl)
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(nil, &db.SkinNotFoundError{})
mocks.Log.EXPECT().IncCounter("signed_textures.request", int64(1)) mocks.Log.EXPECT().IncCounter("signed_textures.request", int64(1))
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/textures/signed/mock_user", nil) mocks.Skins.EXPECT().FindByUsername("mock_user").Return(nil, &db.SkinNotFoundError{})
req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_user", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req) config.CreateHandler().ServeHTTP(w, req)
@ -68,4 +70,72 @@ func TestConfig_SignedTextures2(t *testing.T) {
assert.Equal(204, resp.StatusCode) assert.Equal(204, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body) response, _ := ioutil.ReadAll(resp.Body)
assert.Equal("", string(response)) assert.Equal("", string(response))
})
t.Run("Obtain signed textures for exists user, but without signed textures", func(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
skinModel := createSkinModel("mock_user", false)
skinModel.MojangTextures = ""
skinModel.MojangSignature = ""
mocks.Log.EXPECT().IncCounter("signed_textures.request", int64(1))
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(skinModel, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_user", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(204, resp.StatusCode)
response, _ := ioutil.ReadAll(resp.Body)
assert.Equal("", string(response))
})
t.Run("Obtain signed textures for exists user, but without signed textures", func(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
skinModel := createSkinModel("mock_user", false)
skinModel.MojangTextures = ""
skinModel.MojangSignature = ""
mocks.Log.EXPECT().IncCounter("signed_textures.request", int64(1))
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(skinModel, nil)
mocks.Queue.On("GetTexturesForUsername", "mock_user").Once().Return(createTexturesResponse(true, false))
req := httptest.NewRequest("GET", "http://chrly/textures/signed/mock_user?proxy=true", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(200, resp.StatusCode)
assert.Equal("application/json", resp.Header.Get("Content-Type"))
response, _ := ioutil.ReadAll(resp.Body)
assert.JSONEq(`{
"id": "00000000000000000000000000000000",
"name": "mock_user",
"properties": [
{
"name": "textures",
"value": "eyJ0aW1lc3RhbXAiOjE1NTYzOTg1NzIsInByb2ZpbGVJZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwicHJvZmlsZU5hbWUiOiJtb2NrX3VzZXIiLCJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly9tb2phbmcvc2tpbi5wbmcifX19"
},
{
"name": "chrly",
"value": "how do you tame a horse in Minecraft?"
}
]
}`, string(response))
})
} }

View File

@ -1,6 +1,7 @@
package http package http
import ( import (
"net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -11,7 +12,56 @@ import (
"github.com/elyby/chrly/model" "github.com/elyby/chrly/model"
) )
type skinsTestCase struct {
Name string
RequestUrl string
ExpectedLogKey string
ExistsInLocalStorage bool
ExistsInMojang bool
HasSkinInMojangResp bool
AssertResponse func(assert *testify.Assertions, resp *http.Response)
}
var skinsTestCases = []*skinsTestCase{
{
Name: "Obtain skin for known username",
ExistsInLocalStorage: true,
AssertResponse: func(assert *testify.Assertions, resp *http.Response) {
assert.Equal(301, resp.StatusCode)
assert.Equal("http://chrly/skin.png", resp.Header.Get("Location"))
},
},
{
Name: "Obtain skin for unknown username that exists in Mojang and has a cape",
ExistsInLocalStorage: false,
ExistsInMojang: true,
HasSkinInMojangResp: true,
AssertResponse: func(assert *testify.Assertions, resp *http.Response) {
assert.Equal(301, resp.StatusCode)
assert.Equal("http://mojang/skin.png", resp.Header.Get("Location"))
},
},
{
Name: "Obtain skin for unknown username that exists in Mojang, but don't has a cape",
ExistsInLocalStorage: false,
ExistsInMojang: true,
HasSkinInMojangResp: false,
AssertResponse: func(assert *testify.Assertions, resp *http.Response) {
assert.Equal(404, resp.StatusCode)
},
},
{
Name: "Obtain skin for unknown username that doesn't exists in Mojang",
ExistsInLocalStorage: false,
ExistsInMojang: false,
AssertResponse: func(assert *testify.Assertions, resp *http.Response) {
assert.Equal(404, resp.StatusCode)
},
},
}
func TestConfig_Skin(t *testing.T) { func TestConfig_Skin(t *testing.T) {
performTest := func(t *testing.T, testCase *skinsTestCase) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -19,105 +69,89 @@ func TestConfig_Skin(t *testing.T) {
config, mocks := setupMocks(ctrl) config, mocks := setupMocks(ctrl)
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil) mocks.Log.EXPECT().IncCounter(testCase.ExpectedLogKey, int64(1))
mocks.Log.EXPECT().IncCounter("skins.request", int64(1)) if testCase.ExistsInLocalStorage {
mocks.Skins.EXPECT().FindByUsername("mock_username").Return(createSkinModel("mock_username", false), nil)
} else {
mocks.Skins.EXPECT().FindByUsername("mock_username").Return(nil, &db.SkinNotFoundError{Who: "mock_username"})
}
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins/mock_user", nil) if testCase.ExistsInMojang {
textures := createTexturesResponse(testCase.HasSkinInMojangResp, true)
mocks.Queue.On("GetTexturesForUsername", "mock_username").Return(textures)
} else {
mocks.Queue.On("GetTexturesForUsername", "mock_username").Return(nil)
}
req := httptest.NewRequest("GET", testCase.RequestUrl, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req) config.CreateHandler().ServeHTTP(w, req)
resp := w.Result() resp := w.Result()
assert.Equal(301, resp.StatusCode) testCase.AssertResponse(assert, resp)
assert.Equal("http://ely.by/minecraft/skins/skin.png", resp.Header.Get("Location")) }
}
func TestConfig_Skin2(t *testing.T) { t.Run("Normal API", func(t *testing.T) {
for _, testCase := range skinsTestCases {
testCase.RequestUrl = "http://chrly/skins/mock_username"
testCase.ExpectedLogKey = "skins.request"
t.Run(testCase.Name, func(t *testing.T) {
performTest(t, testCase)
})
}
})
t.Run("GET fallback API", func(t *testing.T) {
for _, testCase := range skinsTestCases {
testCase.RequestUrl = "http://chrly/skins?name=mock_username"
testCase.ExpectedLogKey = "skins.get_request"
t.Run(testCase.Name, func(t *testing.T) {
performTest(t, testCase)
})
}
t.Run("Should trim trailing slash", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) req := httptest.NewRequest("GET", "http://chrly/skins/?name=notch", nil)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
mocks.Skins.EXPECT().FindByUsername("notch").Return(nil, &db.SkinNotFoundError{"notch"})
mocks.Log.EXPECT().IncCounter("skins.request", int64(1))
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins/notch", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(301, resp.StatusCode)
assert.Equal("http://skins.minecraft.net/MinecraftSkins/notch.png", resp.Header.Get("Location"))
}
func TestConfig_SkinGET(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
mocks.Log.EXPECT().IncCounter("skins.get_request", int64(1))
mocks.Log.EXPECT().IncCounter("skins.request", int64(1)).Times(0)
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins?name=mock_user", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(301, resp.StatusCode)
assert.Equal("http://ely.by/minecraft/skins/skin.png", resp.Header.Get("Location"))
}
func TestConfig_SkinGET2(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
mocks.Skins.EXPECT().FindByUsername("notch").Return(nil, &db.SkinNotFoundError{"notch"})
mocks.Log.EXPECT().IncCounter("skins.get_request", int64(1))
mocks.Log.EXPECT().IncCounter("skins.request", int64(1)).Times(0)
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins?name=notch", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(301, resp.StatusCode)
assert.Equal("http://skins.minecraft.net/MinecraftSkins/notch.png", resp.Header.Get("Location"))
}
func TestConfig_SkinGET3(t *testing.T) {
assert := testify.New(t)
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins/?name=notch", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
(&Config{}).CreateHandler().ServeHTTP(w, req) (&Config{}).CreateHandler().ServeHTTP(w, req)
resp := w.Result() resp := w.Result()
assert.Equal(301, resp.StatusCode) assert.Equal(301, resp.StatusCode)
assert.Equal("http://skinsystem.ely.by/skins?name=notch", resp.Header.Get("Location")) assert.Equal("http://chrly/skins?name=notch", resp.Header.Get("Location"))
})
t.Run("Return error when name is not provided", func(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
mocks.Log.EXPECT().IncCounter("skins.get_request", int64(1))
req := httptest.NewRequest("GET", "http://chrly/skins", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(400, resp.StatusCode)
})
})
} }
func createSkinModel(username string, isSlim bool) *model.Skin { func createSkinModel(username string, isSlim bool) *model.Skin {
return &model.Skin{ return &model.Skin{
UserId: 1, UserId: 1,
Username: username, Username: username,
Uuid: "0f657aa8-bfbe-415d-b700-5750090d3af3", Uuid: "0f657aa8-bfbe-415d-b700-5750090d3af3", // Use non nil UUID to pass validation in api tests
SkinId: 1, SkinId: 1,
Hash: "55d2a8848764f5ff04012cdb093458bd", Hash: "00000000000000000000000000000000",
Url: "http://ely.by/minecraft/skins/skin.png", Url: "http://chrly/skin.png",
MojangTextures: "mocked textures base64", MojangTextures: "mocked textures base64",
MojangSignature: "mocked signature", MojangSignature: "mocked signature",
IsSlim: isSlim, IsSlim: isSlim,

View File

@ -14,29 +14,28 @@ func (cfg *Config) Textures(response http.ResponseWriter, request *http.Request)
username := parseUsername(mux.Vars(request)["username"]) username := parseUsername(mux.Vars(request)["username"])
var textures *mojang.TexturesResponse var textures *mojang.TexturesResponse
skin, err := cfg.SkinsRepo.FindByUsername(username) skin, skinErr := cfg.SkinsRepo.FindByUsername(username)
if err == nil && skin.SkinId != 0 { _, capeErr := cfg.CapesRepo.FindByUsername(username)
textures = &mojang.TexturesResponse{ if (skinErr == nil && skin.SkinId != 0) || capeErr == nil {
Skin: &mojang.SkinTexturesResponse{ textures = &mojang.TexturesResponse{}
if skinErr == nil && skin.SkinId != 0 {
skinTextures := &mojang.SkinTexturesResponse{
Url: skin.Url, Url: skin.Url,
},
} }
if skin.IsSlim { if skin.IsSlim {
textures.Skin.Metadata = &mojang.SkinTexturesMetadata{ skinTextures.Metadata = &mojang.SkinTexturesMetadata{
Model: "slim", Model: "slim",
} }
} }
_, err = cfg.CapesRepo.FindByUsername(username) textures.Skin = skinTextures
if err == nil {
var scheme = "http://"
if request.TLS != nil {
scheme = "https://"
} }
if capeErr == nil {
textures.Cape = &mojang.CapeTexturesResponse{ textures.Cape = &mojang.CapeTexturesResponse{
Url: scheme + request.Host + "/cloaks/" + username, Url: request.URL.Scheme + "://" + request.Host + "/cloaks/" + username,
} }
} }
} else { } else {

View File

@ -5,7 +5,6 @@ import (
"io/ioutil" "io/ioutil"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"time"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
testify "github.com/stretchr/testify/assert" testify "github.com/stretchr/testify/assert"
@ -15,6 +14,7 @@ import (
) )
func TestConfig_Textures(t *testing.T) { func TestConfig_Textures(t *testing.T) {
t.Run("Obtain textures for exists user with only default skin", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -22,11 +22,12 @@ func TestConfig_Textures(t *testing.T) {
config, mocks := setupMocks(ctrl) config, mocks := setupMocks(ctrl)
mocks.Log.EXPECT().IncCounter("textures.request", int64(1))
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil) mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
mocks.Capes.EXPECT().FindByUsername("mock_user").Return(nil, &db.CapeNotFoundError{"mock_user"}) mocks.Capes.EXPECT().FindByUsername("mock_user").Return(nil, &db.CapeNotFoundError{Who: "mock_user"})
mocks.Log.EXPECT().IncCounter("textures.request", int64(1))
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/textures/mock_user", nil) req := httptest.NewRequest("GET", "http://chrly/textures/mock_user", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req) config.CreateHandler().ServeHTTP(w, req)
@ -37,13 +38,12 @@ func TestConfig_Textures(t *testing.T) {
response, _ := ioutil.ReadAll(resp.Body) response, _ := ioutil.ReadAll(resp.Body)
assert.JSONEq(`{ assert.JSONEq(`{
"SKIN": { "SKIN": {
"url": "http://ely.by/minecraft/skins/skin.png", "url": "http://chrly/skin.png"
"hash": "55d2a8848764f5ff04012cdb093458bd"
} }
}`, string(response)) }`, string(response))
} })
func TestConfig_Textures2(t *testing.T) { t.Run("Obtain textures for exists user with only slim skin", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -51,11 +51,12 @@ func TestConfig_Textures2(t *testing.T) {
config, mocks := setupMocks(ctrl) config, mocks := setupMocks(ctrl)
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", true), nil)
mocks.Capes.EXPECT().FindByUsername("mock_user").Return(nil, &db.CapeNotFoundError{"mock_user"})
mocks.Log.EXPECT().IncCounter("textures.request", int64(1)) mocks.Log.EXPECT().IncCounter("textures.request", int64(1))
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/textures/mock_user", nil) mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", true), nil)
mocks.Capes.EXPECT().FindByUsername("mock_user").Return(nil, &db.CapeNotFoundError{Who: "mock_user"})
req := httptest.NewRequest("GET", "http://chrly/textures/mock_user", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req) config.CreateHandler().ServeHTTP(w, req)
@ -66,16 +67,15 @@ func TestConfig_Textures2(t *testing.T) {
response, _ := ioutil.ReadAll(resp.Body) response, _ := ioutil.ReadAll(resp.Body)
assert.JSONEq(`{ assert.JSONEq(`{
"SKIN": { "SKIN": {
"url": "http://ely.by/minecraft/skins/skin.png", "url": "http://chrly/skin.png",
"hash": "55d2a8848764f5ff04012cdb093458bd",
"metadata": { "metadata": {
"model": "slim" "model": "slim"
} }
} }
}`, string(response)) }`, string(response))
} })
func TestConfig_Textures3(t *testing.T) { t.Run("Obtain textures for exists user with only cape", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -83,13 +83,41 @@ func TestConfig_Textures3(t *testing.T) {
config, mocks := setupMocks(ctrl) config, mocks := setupMocks(ctrl)
mocks.Log.EXPECT().IncCounter("textures.request", int64(1))
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(nil, &db.SkinNotFoundError{Who: "mock_user"})
mocks.Capes.EXPECT().FindByUsername("mock_user").Return(&model.Cape{File: bytes.NewReader(createCape())}, nil)
req := httptest.NewRequest("GET", "http://chrly/textures/mock_user", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(200, resp.StatusCode)
assert.Equal("application/json", resp.Header.Get("Content-Type"))
response, _ := ioutil.ReadAll(resp.Body)
assert.JSONEq(`{
"CAPE": {
"url": "http://chrly/cloaks/mock_user"
}
}`, string(response))
})
t.Run("Obtain textures for exists user with skin and cape", func(t *testing.T) {
assert := testify.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
config, mocks := setupMocks(ctrl)
mocks.Log.EXPECT().IncCounter("textures.request", int64(1))
mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil) mocks.Skins.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
mocks.Capes.EXPECT().FindByUsername("mock_user").Return(&model.Cape{ mocks.Capes.EXPECT().FindByUsername("mock_user").Return(&model.Cape{File: bytes.NewReader(createCape())}, nil)
File: bytes.NewReader(createCape()),
}, nil)
mocks.Log.EXPECT().IncCounter("textures.request", int64(1))
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/textures/mock_user", nil) req := httptest.NewRequest("GET", "http://chrly/textures/mock_user", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req) config.CreateHandler().ServeHTTP(w, req)
@ -100,17 +128,15 @@ func TestConfig_Textures3(t *testing.T) {
response, _ := ioutil.ReadAll(resp.Body) response, _ := ioutil.ReadAll(resp.Body)
assert.JSONEq(`{ assert.JSONEq(`{
"SKIN": { "SKIN": {
"url": "http://ely.by/minecraft/skins/skin.png", "url": "http://chrly/skin.png"
"hash": "55d2a8848764f5ff04012cdb093458bd"
}, },
"CAPE": { "CAPE": {
"url": "http://skinsystem.ely.by/cloaks/mock_user", "url": "http://chrly/cloaks/mock_user"
"hash": "424ff79dce9940af89c28ad80de8aaad"
} }
}`, string(response)) }`, string(response))
} })
func TestConfig_Textures4(t *testing.T) { t.Run("Obtain textures for not exists user that exists in Mojang", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
@ -118,14 +144,13 @@ func TestConfig_Textures4(t *testing.T) {
config, mocks := setupMocks(ctrl) config, mocks := setupMocks(ctrl)
mocks.Skins.EXPECT().FindByUsername("notch").Return(nil, &db.SkinNotFoundError{})
mocks.Capes.EXPECT().FindByUsername("notch").Return(nil, &db.CapeNotFoundError{})
mocks.Log.EXPECT().IncCounter("textures.request", int64(1)) mocks.Log.EXPECT().IncCounter("textures.request", int64(1))
timeNow = func() time.Time {
return time.Date(2017, time.August, 20, 0, 15, 54, 0, time.UTC)
}
req := httptest.NewRequest("GET", "http://skinsystem.ely.by/textures/notch", nil) mocks.Skins.EXPECT().FindByUsername("mock_username").Return(nil, &db.SkinNotFoundError{})
mocks.Capes.EXPECT().FindByUsername("mock_username").Return(nil, &db.CapeNotFoundError{})
mocks.Queue.On("GetTexturesForUsername", "mock_username").Once().Return(createTexturesResponse(true, true))
req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req) config.CreateHandler().ServeHTTP(w, req)
@ -136,31 +161,36 @@ func TestConfig_Textures4(t *testing.T) {
response, _ := ioutil.ReadAll(resp.Body) response, _ := ioutil.ReadAll(resp.Body)
assert.JSONEq(`{ assert.JSONEq(`{
"SKIN": { "SKIN": {
"url": "http://skins.minecraft.net/MinecraftSkins/notch.png", "url": "http://mojang/skin.png"
"hash": "5923cf3f7fa170a279e4d7a9483cfc52" },
"CAPE": {
"url": "http://mojang/cape.png"
} }
}`, string(response)) }`, string(response))
} })
func TestBuildNonElyTexturesHash(t *testing.T) { t.Run("Obtain textures for not exists user that not exists in Mojang too", func(t *testing.T) {
assert := testify.New(t) assert := testify.New(t)
timeNow = func() time.Time {
return time.Date(2017, time.November, 30, 16, 15, 34, 0, time.UTC)
}
assert.Equal("686d788a5353cb636e8fdff727634d88", buildNonElyTexturesHash("username"), "Function should return fixed hash by username-time pair") ctrl := gomock.NewController(t)
assert.Equal("fb876f761683a10accdb17d403cef64c", buildNonElyTexturesHash("another-username"), "Function should return fixed hash by username-time pair") defer ctrl.Finish()
timeNow = func() time.Time { config, mocks := setupMocks(ctrl)
return time.Date(2017, time.November, 30, 16, 20, 12, 0, time.UTC)
}
assert.Equal("686d788a5353cb636e8fdff727634d88", buildNonElyTexturesHash("username"), "Function should do not change it's value if hour the same") mocks.Log.EXPECT().IncCounter("textures.request", int64(1))
assert.Equal("fb876f761683a10accdb17d403cef64c", buildNonElyTexturesHash("another-username"), "Function should return fixed hash by username-time pair")
timeNow = func() time.Time { mocks.Skins.EXPECT().FindByUsername("mock_username").Return(nil, &db.SkinNotFoundError{})
return time.Date(2017, time.November, 30, 17, 1, 3, 0, time.UTC) mocks.Capes.EXPECT().FindByUsername("mock_username").Return(nil, &db.CapeNotFoundError{})
} mocks.Queue.On("GetTexturesForUsername", "mock_username").Once().Return(nil)
assert.Equal("42277892fd24bc0ed86285b3bb8b8fad", buildNonElyTexturesHash("username"), "Function should change it's value if hour changed") req := httptest.NewRequest("GET", "http://chrly/textures/mock_username", nil)
w := httptest.NewRecorder()
config.CreateHandler().ServeHTTP(w, req)
resp := w.Result()
assert.Equal(204, resp.StatusCode) // TODO: this is not confirmed behavior
})
} }

View File

@ -0,0 +1,33 @@
package tests
import (
"github.com/elyby/chrly/api/mojang"
"github.com/stretchr/testify/mock"
)
type MojangTexturesQueueMock struct {
mock.Mock
}
func (m *MojangTexturesQueueMock) GetTexturesForUsername(username string) chan *mojang.SignedTexturesResponse {
args := m.Called(username)
result := make(chan *mojang.SignedTexturesResponse)
arg := args.Get(0)
switch arg.(type) {
case *mojang.SignedTexturesResponse:
go func() {
result <- arg.(*mojang.SignedTexturesResponse)
}()
case chan *mojang.SignedTexturesResponse:
return arg.(chan *mojang.SignedTexturesResponse)
case nil:
go func() {
result <- nil
}()
default:
panic("unsupported return value")
}
return result
}