mirror of
				https://github.com/elyby/chrly.git
				synced 2025-05-31 14:11:51 +05:30 
			
		
		
		
	Реорганизация пакета daemon в http.
Упразднён пакет utils. Удалён обработчик minecraft.php (legacy с самого-самого начала Ely.by) Добавлены тесты для всех api-запросов.
This commit is contained in:
		
							
								
								
									
										38
									
								
								http/cape.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								http/cape.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) Cape(response http.ResponseWriter, request *http.Request) {
 | 
			
		||||
	if mux.Vars(request)["converted"] == "" {
 | 
			
		||||
		cfg.Logger.IncCounter("capes.request", 1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	username := parseUsername(mux.Vars(request)["username"])
 | 
			
		||||
	rec, err := cfg.CapesRepo.FindByUsername(username)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Redirect(response, request, "http://skins.minecraft.net/MinecraftCloaks/" + username + ".png", 301)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	request.Header.Set("Content-Type", "image/png")
 | 
			
		||||
	io.Copy(response, rec.File)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) CapeGET(response http.ResponseWriter, request *http.Request) {
 | 
			
		||||
	cfg.Logger.IncCounter("capes.get_request", 1)
 | 
			
		||||
	username := request.URL.Query().Get("name")
 | 
			
		||||
	if username == "" {
 | 
			
		||||
		response.WriteHeader(http.StatusBadRequest)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mux.Vars(request)["username"] = username
 | 
			
		||||
	mux.Vars(request)["converted"] = "1"
 | 
			
		||||
 | 
			
		||||
	cfg.Cape(response, request)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										138
									
								
								http/cape_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								http/cape_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/png"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/mock/gomock"
 | 
			
		||||
	testify "github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"elyby/minecraft-skinsystem/db"
 | 
			
		||||
	"elyby/minecraft-skinsystem/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestConfig_Cape(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, _, capesRepo, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	cape := createCape()
 | 
			
		||||
 | 
			
		||||
	capesRepo.EXPECT().FindByUsername("mocked_username").Return(&model.Cape{
 | 
			
		||||
		File: bytes.NewReader(cape),
 | 
			
		||||
	}, nil)
 | 
			
		||||
	wd.EXPECT().IncCounter("capes.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/cloaks/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_Cape2(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, _, capesRepo, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	capesRepo.EXPECT().FindByUsername("notch").Return(nil, &db.CapeNotFoundError{"notch"})
 | 
			
		||||
	wd.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, _, capesRepo, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	cape := createCape()
 | 
			
		||||
 | 
			
		||||
	capesRepo.EXPECT().FindByUsername("mocked_username").Return(&model.Cape{
 | 
			
		||||
		File: bytes.NewReader(cape),
 | 
			
		||||
	}, nil)
 | 
			
		||||
	wd.EXPECT().IncCounter("capes.request", int64(1)).Times(0)
 | 
			
		||||
	wd.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, _, capesRepo, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	capesRepo.EXPECT().FindByUsername("notch").Return(nil, &db.CapeNotFoundError{"notch"})
 | 
			
		||||
	wd.EXPECT().IncCounter("capes.request", int64(1)).Times(0)
 | 
			
		||||
	wd.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()
 | 
			
		||||
 | 
			
		||||
	(&Config{}).CreateHandler().ServeHTTP(w, req)
 | 
			
		||||
 | 
			
		||||
	resp := w.Result()
 | 
			
		||||
	assert.Equal(301, resp.StatusCode)
 | 
			
		||||
	assert.Equal("http://skinsystem.ely.by/cloaks?name=notch", resp.Header.Get("Location"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cape md5: 424ff79dce9940af89c28ad80de8aaad
 | 
			
		||||
func createCape() []byte {
 | 
			
		||||
	img := image.NewAlpha(image.Rect(0, 0, 64, 32))
 | 
			
		||||
	writer := &bytes.Buffer{}
 | 
			
		||||
	png.Encode(writer, img)
 | 
			
		||||
 | 
			
		||||
	pngBytes, _ := ioutil.ReadAll(writer)
 | 
			
		||||
 | 
			
		||||
	return pngBytes
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								http/face.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								http/face.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const defaultHash = "default"
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) Face(response http.ResponseWriter, request *http.Request) {
 | 
			
		||||
	cfg.Logger.IncCounter("faces.request", 1)
 | 
			
		||||
	username := parseUsername(mux.Vars(request)["username"])
 | 
			
		||||
	rec, err := cfg.SkinsRepo.FindByUsername(username)
 | 
			
		||||
	var hash string
 | 
			
		||||
	if err != nil || rec.SkinId == 0 {
 | 
			
		||||
		hash = defaultHash
 | 
			
		||||
	} else {
 | 
			
		||||
		hash = rec.Hash
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	http.Redirect(response, request, buildElyUrl(buildFaceUrl(hash)), 301)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildFaceUrl(hash string) string {
 | 
			
		||||
	return "/minecraft/skin_buffer/faces/" + hash + ".png"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								http/face_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								http/face_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/mock/gomock"
 | 
			
		||||
	testify "github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"elyby/minecraft-skinsystem/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestConfig_Face(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, _, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
 | 
			
		||||
	wd.EXPECT().IncCounter("faces.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins/mock_user/face.png", nil)
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
	config.CreateHandler().ServeHTTP(w, req)
 | 
			
		||||
 | 
			
		||||
	resp := w.Result()
 | 
			
		||||
	assert.Equal(301, resp.StatusCode)
 | 
			
		||||
	assert.Equal("http://ely.by/minecraft/skin_buffer/faces/55d2a8848764f5ff04012cdb093458bd.png", resp.Header.Get("Location"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestConfig_Face2(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, _, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(nil, &db.SkinNotFoundError{"mock_user"})
 | 
			
		||||
	wd.EXPECT().IncCounter("faces.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins/mock_user/face.png", nil)
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
	config.CreateHandler().ServeHTTP(w, req)
 | 
			
		||||
 | 
			
		||||
	resp := w.Result()
 | 
			
		||||
	assert.Equal(301, resp.StatusCode)
 | 
			
		||||
	assert.Equal("http://ely.by/minecraft/skin_buffer/faces/default.png", resp.Header.Get("Location"))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										91
									
								
								http/http.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								http/http.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/mono83/slf/wd"
 | 
			
		||||
 | 
			
		||||
	"elyby/minecraft-skinsystem/interfaces"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	ListenSpec string
 | 
			
		||||
 | 
			
		||||
	SkinsRepo interfaces.SkinsRepository
 | 
			
		||||
	CapesRepo interfaces.CapesRepository
 | 
			
		||||
	Logger    wd.Watchdog
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) Run() error {
 | 
			
		||||
	cfg.Logger.Info(fmt.Sprintf("Starting, HTTP on: %s\n", cfg.ListenSpec))
 | 
			
		||||
 | 
			
		||||
	listener, err := net.Listen("tcp", cfg.ListenSpec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server := &http.Server{
 | 
			
		||||
		ReadTimeout:    60 * time.Second,
 | 
			
		||||
		WriteTimeout:   60 * time.Second,
 | 
			
		||||
		MaxHeaderBytes: 1 << 16,
 | 
			
		||||
		Handler:        cfg.CreateHandler(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go server.Serve(listener)
 | 
			
		||||
 | 
			
		||||
	s := waitForSignal()
 | 
			
		||||
	cfg.Logger.Info(fmt.Sprintf("Got signal: %v, exiting.", s))
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) CreateHandler() http.Handler {
 | 
			
		||||
	router := mux.NewRouter().StrictSlash(true)
 | 
			
		||||
 | 
			
		||||
	router.HandleFunc("/skins/{username}", cfg.Skin).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/cloaks/{username}", cfg.Cape).Methods("GET").Name("cloaks")
 | 
			
		||||
	router.HandleFunc("/textures/{username}", cfg.Textures).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/textures/signed/{username}", cfg.SignedTextures).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/skins/{username}/face", cfg.Face).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/skins/{username}/face.png", cfg.Face).Methods("GET")
 | 
			
		||||
	// Legacy
 | 
			
		||||
	router.HandleFunc("/skins", cfg.SkinGET).Methods("GET")
 | 
			
		||||
	router.HandleFunc("/cloaks", cfg.CapeGET).Methods("GET")
 | 
			
		||||
	// 404
 | 
			
		||||
	router.NotFoundHandler = http.HandlerFunc(cfg.NotFound)
 | 
			
		||||
 | 
			
		||||
	return router
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseUsername(username string) string {
 | 
			
		||||
	const suffix = ".png"
 | 
			
		||||
	if strings.HasSuffix(username, suffix) {
 | 
			
		||||
		username = strings.TrimSuffix(username, suffix)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return username
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildElyUrl(route string) string {
 | 
			
		||||
	prefix := "http://ely.by"
 | 
			
		||||
	if !strings.HasPrefix(route, prefix) {
 | 
			
		||||
		route = prefix + route
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return route
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func waitForSignal() os.Signal {
 | 
			
		||||
	ch := make(chan os.Signal)
 | 
			
		||||
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
 | 
			
		||||
 | 
			
		||||
	return <-ch
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								http/http_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								http/http_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/mock/gomock"
 | 
			
		||||
	testify "github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"elyby/minecraft-skinsystem/http/mock_wd"
 | 
			
		||||
	"elyby/minecraft-skinsystem/interfaces/mock_interfaces"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestParseUsername(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
	assert.Equal("test", parseUsername("test.png"), "Function should trim .png at end")
 | 
			
		||||
	assert.Equal("test", parseUsername("test"), "Function should return string itself, if it not contains .png at end")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuildElyUrl(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
	assert.Equal("http://ely.by/route", buildElyUrl("/route"), "Function should add prefix to the provided relative url.")
 | 
			
		||||
	assert.Equal("http://ely.by/test/route", buildElyUrl("http://ely.by/test/route"), "Function should do not add prefix to the provided prefixed url.")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setupMocks(ctrl *gomock.Controller) (
 | 
			
		||||
	*Config,
 | 
			
		||||
	*mock_interfaces.MockSkinsRepository,
 | 
			
		||||
	*mock_interfaces.MockCapesRepository,
 | 
			
		||||
	*mock_wd.MockWatchdog,
 | 
			
		||||
) {
 | 
			
		||||
	skinsRepo := mock_interfaces.NewMockSkinsRepository(ctrl)
 | 
			
		||||
	capesRepo := mock_interfaces.NewMockCapesRepository(ctrl)
 | 
			
		||||
	wd := mock_wd.NewMockWatchdog(ctrl)
 | 
			
		||||
 | 
			
		||||
	return &Config{
 | 
			
		||||
		SkinsRepo: skinsRepo,
 | 
			
		||||
		CapesRepo: capesRepo,
 | 
			
		||||
		Logger: wd,
 | 
			
		||||
	}, skinsRepo, capesRepo, wd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										218
									
								
								http/mock_wd/mock_wd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								http/mock_wd/mock_wd.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,218 @@
 | 
			
		||||
// Code generated by MockGen. DO NOT EDIT.
 | 
			
		||||
// Source: github.com/mono83/slf/wd (interfaces: Watchdog)
 | 
			
		||||
 | 
			
		||||
package mock_wd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	gomock "github.com/golang/mock/gomock"
 | 
			
		||||
	slf "github.com/mono83/slf"
 | 
			
		||||
	wd "github.com/mono83/slf/wd"
 | 
			
		||||
	reflect "reflect"
 | 
			
		||||
	time "time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MockWatchdog is a mock of Watchdog interface
 | 
			
		||||
type MockWatchdog struct {
 | 
			
		||||
	ctrl     *gomock.Controller
 | 
			
		||||
	recorder *MockWatchdogMockRecorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MockWatchdogMockRecorder is the mock recorder for MockWatchdog
 | 
			
		||||
type MockWatchdogMockRecorder struct {
 | 
			
		||||
	mock *MockWatchdog
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMockWatchdog creates a new mock instance
 | 
			
		||||
func NewMockWatchdog(ctrl *gomock.Controller) *MockWatchdog {
 | 
			
		||||
	mock := &MockWatchdog{ctrl: ctrl}
 | 
			
		||||
	mock.recorder = &MockWatchdogMockRecorder{mock}
 | 
			
		||||
	return mock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EXPECT returns an object that allows the caller to indicate expected use
 | 
			
		||||
func (_m *MockWatchdog) EXPECT() *MockWatchdogMockRecorder {
 | 
			
		||||
	return _m.recorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Alert mocks base method
 | 
			
		||||
func (_m *MockWatchdog) Alert(_param0 string, _param1 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0}
 | 
			
		||||
	for _, _x := range _param1 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "Alert", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Alert indicates an expected call of Alert
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) Alert(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0}, arg1...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Alert", reflect.TypeOf((*MockWatchdog)(nil).Alert), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debug mocks base method
 | 
			
		||||
func (_m *MockWatchdog) Debug(_param0 string, _param1 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0}
 | 
			
		||||
	for _, _x := range _param1 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "Debug", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debug indicates an expected call of Debug
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) Debug(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0}, arg1...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Debug", reflect.TypeOf((*MockWatchdog)(nil).Debug), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Emergency mocks base method
 | 
			
		||||
func (_m *MockWatchdog) Emergency(_param0 string, _param1 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0}
 | 
			
		||||
	for _, _x := range _param1 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "Emergency", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Emergency indicates an expected call of Emergency
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) Emergency(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0}, arg1...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Emergency", reflect.TypeOf((*MockWatchdog)(nil).Emergency), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error mocks base method
 | 
			
		||||
func (_m *MockWatchdog) Error(_param0 string, _param1 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0}
 | 
			
		||||
	for _, _x := range _param1 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "Error", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error indicates an expected call of Error
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) Error(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0}, arg1...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Error", reflect.TypeOf((*MockWatchdog)(nil).Error), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IncCounter mocks base method
 | 
			
		||||
func (_m *MockWatchdog) IncCounter(_param0 string, _param1 int64, _param2 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0, _param1}
 | 
			
		||||
	for _, _x := range _param2 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "IncCounter", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IncCounter indicates an expected call of IncCounter
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) IncCounter(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0, arg1}, arg2...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "IncCounter", reflect.TypeOf((*MockWatchdog)(nil).IncCounter), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info mocks base method
 | 
			
		||||
func (_m *MockWatchdog) Info(_param0 string, _param1 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0}
 | 
			
		||||
	for _, _x := range _param1 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "Info", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info indicates an expected call of Info
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) Info(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0}, arg1...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Info", reflect.TypeOf((*MockWatchdog)(nil).Info), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RecordTimer mocks base method
 | 
			
		||||
func (_m *MockWatchdog) RecordTimer(_param0 string, _param1 time.Duration, _param2 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0, _param1}
 | 
			
		||||
	for _, _x := range _param2 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "RecordTimer", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RecordTimer indicates an expected call of RecordTimer
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) RecordTimer(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0, arg1}, arg2...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "RecordTimer", reflect.TypeOf((*MockWatchdog)(nil).RecordTimer), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Timer mocks base method
 | 
			
		||||
func (_m *MockWatchdog) Timer(_param0 string, _param1 ...slf.Param) slf.Timer {
 | 
			
		||||
	_s := []interface{}{_param0}
 | 
			
		||||
	for _, _x := range _param1 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	ret := _m.ctrl.Call(_m, "Timer", _s...)
 | 
			
		||||
	ret0, _ := ret[0].(slf.Timer)
 | 
			
		||||
	return ret0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Timer indicates an expected call of Timer
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) Timer(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0}, arg1...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Timer", reflect.TypeOf((*MockWatchdog)(nil).Timer), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Trace mocks base method
 | 
			
		||||
func (_m *MockWatchdog) Trace(_param0 string, _param1 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0}
 | 
			
		||||
	for _, _x := range _param1 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "Trace", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Trace indicates an expected call of Trace
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) Trace(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0}, arg1...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Trace", reflect.TypeOf((*MockWatchdog)(nil).Trace), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateGauge mocks base method
 | 
			
		||||
func (_m *MockWatchdog) UpdateGauge(_param0 string, _param1 int64, _param2 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0, _param1}
 | 
			
		||||
	for _, _x := range _param2 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "UpdateGauge", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateGauge indicates an expected call of UpdateGauge
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) UpdateGauge(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0, arg1}, arg2...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "UpdateGauge", reflect.TypeOf((*MockWatchdog)(nil).UpdateGauge), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warning mocks base method
 | 
			
		||||
func (_m *MockWatchdog) Warning(_param0 string, _param1 ...slf.Param) {
 | 
			
		||||
	_s := []interface{}{_param0}
 | 
			
		||||
	for _, _x := range _param1 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	_m.ctrl.Call(_m, "Warning", _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Warning indicates an expected call of Warning
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) Warning(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 | 
			
		||||
	_s := append([]interface{}{arg0}, arg1...)
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Warning", reflect.TypeOf((*MockWatchdog)(nil).Warning), _s...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithParams mocks base method
 | 
			
		||||
func (_m *MockWatchdog) WithParams(_param0 ...slf.Param) wd.Watchdog {
 | 
			
		||||
	_s := []interface{}{}
 | 
			
		||||
	for _, _x := range _param0 {
 | 
			
		||||
		_s = append(_s, _x)
 | 
			
		||||
	}
 | 
			
		||||
	ret := _m.ctrl.Call(_m, "WithParams", _s...)
 | 
			
		||||
	ret0, _ := ret[0].(wd.Watchdog)
 | 
			
		||||
	return ret0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithParams indicates an expected call of WithParams
 | 
			
		||||
func (_mr *MockWatchdogMockRecorder) WithParams(arg0 ...interface{}) *gomock.Call {
 | 
			
		||||
	return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "WithParams", reflect.TypeOf((*MockWatchdog)(nil).WithParams), arg0...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								http/not_found.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								http/not_found.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) NotFound(response http.ResponseWriter, request *http.Request)  {
 | 
			
		||||
	data, _ := json.Marshal(map[string]string{
 | 
			
		||||
		"status": "404",
 | 
			
		||||
		"message": "Not Found",
 | 
			
		||||
		"link": "http://docs.ely.by/skin-system.html",
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	response.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	response.WriteHeader(http.StatusNotFound)
 | 
			
		||||
	response.Write(data)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								http/not_found_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								http/not_found_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	testify "github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestConfig_NotFound(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/", nil)
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
	(&Config{}).CreateHandler().ServeHTTP(w, req)
 | 
			
		||||
 | 
			
		||||
	resp := w.Result()
 | 
			
		||||
	assert.Equal(404, resp.StatusCode)
 | 
			
		||||
	assert.Equal("application/json", resp.Header.Get("Content-Type"))
 | 
			
		||||
	response, _ := ioutil.ReadAll(resp.Body)
 | 
			
		||||
	assert.JSONEq(`{
 | 
			
		||||
		"status": "404",
 | 
			
		||||
		"message": "Not Found",
 | 
			
		||||
		"link": "http://docs.ely.by/skin-system.html"
 | 
			
		||||
	}`, string(response))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								http/signed_textures.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								http/signed_textures.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type signedTexturesResponse struct {
 | 
			
		||||
	Id    string     `json:"id"`
 | 
			
		||||
	Name  string     `json:"name"`
 | 
			
		||||
	IsEly bool       `json:"ely,omitempty"`
 | 
			
		||||
	Props []property `json:"properties"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type property struct {
 | 
			
		||||
	Name      string `json:"name"`
 | 
			
		||||
	Signature string `json:"signature,omitempty"`
 | 
			
		||||
	Value     string `json:"value"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) SignedTextures(response http.ResponseWriter, request *http.Request) {
 | 
			
		||||
	cfg.Logger.IncCounter("signed_textures.request", 1)
 | 
			
		||||
	username := parseUsername(mux.Vars(request)["username"])
 | 
			
		||||
 | 
			
		||||
	rec, err := cfg.SkinsRepo.FindByUsername(username)
 | 
			
		||||
	if err != nil || rec.SkinId == 0 || rec.MojangTextures == "" {
 | 
			
		||||
		response.WriteHeader(http.StatusNoContent)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	responseData:= signedTexturesResponse{
 | 
			
		||||
		Id: strings.Replace(rec.Uuid, "-", "", -1),
 | 
			
		||||
		Name: rec.Username,
 | 
			
		||||
		Props: []property{
 | 
			
		||||
			{
 | 
			
		||||
				Name: "textures",
 | 
			
		||||
				Signature: rec.MojangSignature,
 | 
			
		||||
				Value: rec.MojangTextures,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Name: "ely",
 | 
			
		||||
				Value: "but why are you asking?",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	responseJson,_ := json.Marshal(responseData)
 | 
			
		||||
	response.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	response.Write(responseJson)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								http/signed_textures_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								http/signed_textures_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/mock/gomock"
 | 
			
		||||
	testify "github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"elyby/minecraft-skinsystem/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestConfig_SignedTextures(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, _, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
 | 
			
		||||
	wd.EXPECT().IncCounter("signed_textures.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/textures/signed/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(`{
 | 
			
		||||
		"id": "0f657aa8bfbe415db7005750090d3af3",
 | 
			
		||||
		"name": "mock_user",
 | 
			
		||||
		"properties": [
 | 
			
		||||
			{
 | 
			
		||||
				"name": "textures",
 | 
			
		||||
				"signature": "mocked signature",
 | 
			
		||||
				"value": "mocked textures base64"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"name": "ely",
 | 
			
		||||
				"value": "but why are you asking?"
 | 
			
		||||
			}
 | 
			
		||||
		]
 | 
			
		||||
	}`, string(response))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestConfig_SignedTextures2(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, _, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(nil, &db.SkinNotFoundError{})
 | 
			
		||||
	wd.EXPECT().IncCounter("signed_textures.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/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))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								http/skin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								http/skin.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) Skin(response http.ResponseWriter, request *http.Request) {
 | 
			
		||||
	if mux.Vars(request)["converted"] == "" {
 | 
			
		||||
		cfg.Logger.IncCounter("skins.request", 1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	username := parseUsername(mux.Vars(request)["username"])
 | 
			
		||||
	rec, err := cfg.SkinsRepo.FindByUsername(username)
 | 
			
		||||
	if err != nil || rec.SkinId == 0 {
 | 
			
		||||
		http.Redirect(response, request, "http://skins.minecraft.net/MinecraftSkins/" + username + ".png", 301)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	http.Redirect(response, request, buildElyUrl(rec.Url), 301)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) SkinGET(response http.ResponseWriter, request *http.Request) {
 | 
			
		||||
	cfg.Logger.IncCounter("skins.get_request", 1)
 | 
			
		||||
	username := request.URL.Query().Get("name")
 | 
			
		||||
	if username == "" {
 | 
			
		||||
		response.WriteHeader(http.StatusBadRequest)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mux.Vars(request)["username"] = username
 | 
			
		||||
	mux.Vars(request)["converted"] = "1"
 | 
			
		||||
 | 
			
		||||
	cfg.Skin(response, request)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										124
									
								
								http/skin_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								http/skin_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/mock/gomock"
 | 
			
		||||
	testify "github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"elyby/minecraft-skinsystem/db"
 | 
			
		||||
	"elyby/minecraft-skinsystem/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestConfig_Skin(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, _, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
 | 
			
		||||
	wd.EXPECT().IncCounter("skins.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/skins/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_Skin2(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, _, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("notch").Return(nil, &db.SkinNotFoundError{"notch"})
 | 
			
		||||
	wd.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, skinsRepo, _, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
 | 
			
		||||
	wd.EXPECT().IncCounter("skins.get_request", int64(1))
 | 
			
		||||
	wd.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, skinsRepo, _, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("notch").Return(nil, &db.SkinNotFoundError{"notch"})
 | 
			
		||||
	wd.EXPECT().IncCounter("skins.get_request", int64(1))
 | 
			
		||||
	wd.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()
 | 
			
		||||
 | 
			
		||||
	(&Config{}).CreateHandler().ServeHTTP(w, req)
 | 
			
		||||
 | 
			
		||||
	resp := w.Result()
 | 
			
		||||
	assert.Equal(301, resp.StatusCode)
 | 
			
		||||
	assert.Equal("http://skinsystem.ely.by/skins?name=notch", resp.Header.Get("Location"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createSkinModel(username string, isSlim bool) *model.Skin {
 | 
			
		||||
	return &model.Skin{
 | 
			
		||||
		Username:        username,
 | 
			
		||||
		Uuid:            "0f657aa8-bfbe-415d-b700-5750090d3af3",
 | 
			
		||||
		SkinId:          1,
 | 
			
		||||
		Hash:            "55d2a8848764f5ff04012cdb093458bd",
 | 
			
		||||
		Url:             "http://ely.by/minecraft/skins/skin.png",
 | 
			
		||||
		MojangTextures:  "mocked textures base64",
 | 
			
		||||
		MojangSignature: "mocked signature",
 | 
			
		||||
		IsSlim:          isSlim,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										104
									
								
								http/textures.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								http/textures.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
 | 
			
		||||
	"elyby/minecraft-skinsystem/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type texturesResponse struct {
 | 
			
		||||
	Skin *Skin `json:"SKIN"`
 | 
			
		||||
	Cape *Cape `json:"CAPE,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Skin struct {
 | 
			
		||||
	Url      string        `json:"url"`
 | 
			
		||||
	Hash     string        `json:"hash"`
 | 
			
		||||
	Metadata *skinMetadata `json:"metadata,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type skinMetadata struct {
 | 
			
		||||
	Model string `json:"model"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Cape struct {
 | 
			
		||||
	Url  string `json:"url"`
 | 
			
		||||
	Hash string `json:"hash"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cfg *Config) Textures(response http.ResponseWriter, request *http.Request) {
 | 
			
		||||
	cfg.Logger.IncCounter("textures.request", 1)
 | 
			
		||||
	username := parseUsername(mux.Vars(request)["username"])
 | 
			
		||||
 | 
			
		||||
	skin, err := cfg.SkinsRepo.FindByUsername(username)
 | 
			
		||||
	if err != nil || skin.SkinId == 0 {
 | 
			
		||||
		if skin == nil {
 | 
			
		||||
			skin = &model.Skin{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		skin.Url = "http://skins.minecraft.net/MinecraftSkins/" + username + ".png"
 | 
			
		||||
		skin.Hash = string(buildNonElyTexturesHash(username))
 | 
			
		||||
	} else {
 | 
			
		||||
		skin.Url = buildElyUrl(skin.Url)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	textures := texturesResponse{
 | 
			
		||||
		Skin: &Skin{
 | 
			
		||||
			Url:  skin.Url,
 | 
			
		||||
			Hash: skin.Hash,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if skin.IsSlim {
 | 
			
		||||
		textures.Skin.Metadata = &skinMetadata{
 | 
			
		||||
			Model: "slim",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cape, err := cfg.CapesRepo.FindByUsername(username)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		var scheme string = "http://"
 | 
			
		||||
		if request.TLS != nil {
 | 
			
		||||
			scheme = "https://"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		textures.Cape = &Cape{
 | 
			
		||||
			Url:  scheme + request.Host + "/cloaks/" + username,
 | 
			
		||||
			Hash: calculateCapeHash(cape),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	responseData, _ := json.Marshal(textures)
 | 
			
		||||
	response.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	response.Write(responseData)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func calculateCapeHash(cape *model.Cape) string {
 | 
			
		||||
	hasher := md5.New()
 | 
			
		||||
	io.Copy(hasher, cape.File)
 | 
			
		||||
 | 
			
		||||
	return hex.EncodeToString(hasher.Sum(nil))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildNonElyTexturesHash(username string) string {
 | 
			
		||||
	hour := getCurrentHour()
 | 
			
		||||
	hasher := md5.New()
 | 
			
		||||
	hasher.Write([]byte("non-ely-" + strconv.FormatInt(hour, 10) + "-" + username))
 | 
			
		||||
 | 
			
		||||
	return hex.EncodeToString(hasher.Sum(nil))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var timeNow = time.Now
 | 
			
		||||
 | 
			
		||||
func getCurrentHour() int64 {
 | 
			
		||||
	n := timeNow()
 | 
			
		||||
	return time.Date(n.Year(), n.Month(), n.Day(), n.Hour(), 0, 0, 0, time.UTC).Unix()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										166
									
								
								http/textures_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								http/textures_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/mock/gomock"
 | 
			
		||||
	testify "github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"elyby/minecraft-skinsystem/db"
 | 
			
		||||
	"elyby/minecraft-skinsystem/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestConfig_Textures(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, capesRepo, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
 | 
			
		||||
	capesRepo.EXPECT().FindByUsername("mock_user").Return(nil, &db.CapeNotFoundError{"mock_user"})
 | 
			
		||||
	wd.EXPECT().IncCounter("textures.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/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(`{
 | 
			
		||||
		"SKIN": {
 | 
			
		||||
			"url": "http://ely.by/minecraft/skins/skin.png",
 | 
			
		||||
			"hash": "55d2a8848764f5ff04012cdb093458bd"
 | 
			
		||||
		}
 | 
			
		||||
	}`, string(response))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestConfig_Textures2(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, capesRepo, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", true), nil)
 | 
			
		||||
	capesRepo.EXPECT().FindByUsername("mock_user").Return(nil, &db.CapeNotFoundError{"mock_user"})
 | 
			
		||||
	wd.EXPECT().IncCounter("textures.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/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(`{
 | 
			
		||||
		"SKIN": {
 | 
			
		||||
			"url": "http://ely.by/minecraft/skins/skin.png",
 | 
			
		||||
			"hash": "55d2a8848764f5ff04012cdb093458bd",
 | 
			
		||||
			"metadata": {
 | 
			
		||||
				"model": "slim"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}`, string(response))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestConfig_Textures3(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, capesRepo, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("mock_user").Return(createSkinModel("mock_user", false), nil)
 | 
			
		||||
	capesRepo.EXPECT().FindByUsername("mock_user").Return(&model.Cape{
 | 
			
		||||
		File: bytes.NewReader(createCape()),
 | 
			
		||||
	}, nil)
 | 
			
		||||
	wd.EXPECT().IncCounter("textures.request", int64(1))
 | 
			
		||||
 | 
			
		||||
	req := httptest.NewRequest("GET", "http://skinsystem.ely.by/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(`{
 | 
			
		||||
		"SKIN": {
 | 
			
		||||
			"url": "http://ely.by/minecraft/skins/skin.png",
 | 
			
		||||
			"hash": "55d2a8848764f5ff04012cdb093458bd"
 | 
			
		||||
		},
 | 
			
		||||
		"CAPE": {
 | 
			
		||||
			"url": "http://skinsystem.ely.by/cloaks/mock_user",
 | 
			
		||||
			"hash": "424ff79dce9940af89c28ad80de8aaad"
 | 
			
		||||
		}
 | 
			
		||||
	}`, string(response))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestConfig_Textures4(t *testing.T) {
 | 
			
		||||
	assert := testify.New(t)
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	config, skinsRepo, capesRepo, wd := setupMocks(ctrl)
 | 
			
		||||
 | 
			
		||||
	skinsRepo.EXPECT().FindByUsername("notch").Return(nil, &db.SkinNotFoundError{})
 | 
			
		||||
	capesRepo.EXPECT().FindByUsername("notch").Return(nil, &db.CapeNotFoundError{})
 | 
			
		||||
	wd.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)
 | 
			
		||||
	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(`{
 | 
			
		||||
		"SKIN": {
 | 
			
		||||
			"url": "http://skins.minecraft.net/MinecraftSkins/notch.png",
 | 
			
		||||
			"hash": "5923cf3f7fa170a279e4d7a9483cfc52"
 | 
			
		||||
		}
 | 
			
		||||
	}`, string(response))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBuildNonElyTexturesHash(t *testing.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")
 | 
			
		||||
	assert.Equal("fb876f761683a10accdb17d403cef64c", buildNonElyTexturesHash("another-username"), "Function should return fixed hash by username-time pair")
 | 
			
		||||
 | 
			
		||||
	timeNow = func() time.Time {
 | 
			
		||||
		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")
 | 
			
		||||
	assert.Equal("fb876f761683a10accdb17d403cef64c", buildNonElyTexturesHash("another-username"), "Function should return fixed hash by username-time pair")
 | 
			
		||||
 | 
			
		||||
	timeNow = func() time.Time {
 | 
			
		||||
		return time.Date(2017, time.November, 30, 17, 1, 3, 0, time.UTC)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	assert.Equal("42277892fd24bc0ed86285b3bb8b8fad", buildNonElyTexturesHash("username"), "Function should change it's value if hour changed")
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user