mirror of
https://github.com/elyby/chrly.git
synced 2024-11-26 16:51:59 +05:30
Merge branch 'db_rework'
This commit is contained in:
commit
6453583e31
@ -22,8 +22,10 @@ jobs:
|
||||
# Tests stage
|
||||
- name: Unit tests
|
||||
stage: Tests
|
||||
services:
|
||||
- redis
|
||||
script:
|
||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
- go test -v -race --tags redis -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
# FOSSA is don't feel so good, let it rest
|
||||
|
@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- `ely.skinsystem.{hostname}.app.mojang_textures.usernames.textures_miss`
|
||||
- All incoming requests are now logging to the console in
|
||||
[Apache Common Log Format](http://httpd.apache.org/docs/2.2/logs.html#common).
|
||||
- Added `/healthcheck` endpoint (at the moment checks are only available for the batch Mojang UUIDs provider).
|
||||
- Added `/healthcheck` endpoint.
|
||||
- Graceful server shutdown.
|
||||
- Panics in http are now logged in Sentry.
|
||||
|
||||
|
1
Gopkg.lock
generated
1
Gopkg.lock
generated
@ -350,6 +350,7 @@
|
||||
"github.com/spf13/viper",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/mock",
|
||||
"github.com/stretchr/testify/require",
|
||||
"github.com/stretchr/testify/suite",
|
||||
"github.com/tevino/abool",
|
||||
"github.com/thedevsaddam/govalidator",
|
||||
|
@ -1,9 +0,0 @@
|
||||
package db
|
||||
|
||||
type ParamRequired struct {
|
||||
Param string
|
||||
}
|
||||
|
||||
func (e ParamRequired) Error() string {
|
||||
return "Required parameter not provided"
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/elyby/chrly/http"
|
||||
"github.com/elyby/chrly/mojangtextures"
|
||||
)
|
||||
|
||||
type RepositoriesCreator interface {
|
||||
CreateSkinsRepository() (http.SkinsRepository, error)
|
||||
CreateCapesRepository() (http.CapesRepository, error)
|
||||
CreateMojangUuidsRepository() (mojangtextures.UuidsStorage, error)
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/elyby/chrly/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/elyby/chrly/model"
|
||||
"github.com/elyby/chrly/mojangtextures"
|
||||
)
|
||||
|
||||
type FilesystemFactory struct {
|
||||
BasePath string
|
||||
CapesDirName string
|
||||
}
|
||||
|
||||
func (f FilesystemFactory) CreateSkinsRepository() (http.SkinsRepository, error) {
|
||||
panic("skins repository not supported for this storage type")
|
||||
}
|
||||
|
||||
func (f FilesystemFactory) CreateCapesRepository() (http.CapesRepository, error) {
|
||||
if err := f.validateFactoryConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &filesStorage{path: path.Join(f.BasePath, f.CapesDirName)}, nil
|
||||
}
|
||||
|
||||
func (f FilesystemFactory) CreateMojangUuidsRepository() (mojangtextures.UuidsStorage, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (f FilesystemFactory) validateFactoryConfig() error {
|
||||
if f.BasePath == "" {
|
||||
return &ParamRequired{"basePath"}
|
||||
}
|
||||
|
||||
if f.CapesDirName == "" {
|
||||
f.CapesDirName = "capes"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type filesStorage struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (repository *filesStorage) FindByUsername(username string) (*model.Cape, error) {
|
||||
if username == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
capePath := path.Join(repository.path, strings.ToLower(username)+".png")
|
||||
file, err := os.Open(capePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.Cape{
|
||||
File: file,
|
||||
}, nil
|
||||
}
|
33
db/fs/fs.go
Normal file
33
db/fs/fs.go
Normal file
@ -0,0 +1,33 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/elyby/chrly/model"
|
||||
)
|
||||
|
||||
func New(basePath string) (*Filesystem, error) {
|
||||
return &Filesystem{path: basePath}, nil
|
||||
}
|
||||
|
||||
type Filesystem struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (f *Filesystem) FindCapeByUsername(username string) (*model.Cape, error) {
|
||||
capePath := path.Join(f.path, strings.ToLower(username)+".png")
|
||||
file, err := os.Open(capePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.Cape{
|
||||
File: file,
|
||||
}, nil
|
||||
}
|
56
db/fs/fs_integration_test.go
Normal file
56
db/fs/fs_integration_test.go
Normal file
@ -0,0 +1,56 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
fs, err := New("base/path")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "base/path", fs.path)
|
||||
}
|
||||
|
||||
func TestFilesystem(t *testing.T) {
|
||||
t.Run("FindCapeByUsername", func(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "capes")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot crete temp directory for tests: %w", err))
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
t.Run("exists cape", func(t *testing.T) {
|
||||
file, err := os.Create(path.Join(dir, "username.png"))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot create temp skin for tests: %w", err))
|
||||
}
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
fs, _ := New(dir)
|
||||
cape, err := fs.FindCapeByUsername("username")
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, cape)
|
||||
capeFile, _ := cape.File.(*os.File)
|
||||
require.Equal(t, file.Name(), capeFile.Name())
|
||||
})
|
||||
|
||||
t.Run("not exists cape", func(t *testing.T) {
|
||||
fs, _ := New(dir)
|
||||
cape, err := fs.FindCapeByUsername("username")
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, cape)
|
||||
})
|
||||
|
||||
t.Run("empty username", func(t *testing.T) {
|
||||
fs, _ := New(dir)
|
||||
cape, err := fs.FindCapeByUsername("")
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, cape)
|
||||
})
|
||||
})
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
package db
|
||||
package redis
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -14,69 +13,31 @@ import (
|
||||
"github.com/mediocregopher/radix.v2/redis"
|
||||
"github.com/mediocregopher/radix.v2/util"
|
||||
|
||||
"github.com/elyby/chrly/http"
|
||||
"github.com/elyby/chrly/model"
|
||||
"github.com/elyby/chrly/mojangtextures"
|
||||
)
|
||||
|
||||
type RedisFactory struct {
|
||||
Host string
|
||||
Port int
|
||||
PoolSize int
|
||||
pool *pool.Pool
|
||||
}
|
||||
var now = time.Now
|
||||
|
||||
func (f *RedisFactory) CreateSkinsRepository() (http.SkinsRepository, error) {
|
||||
return f.createInstance()
|
||||
}
|
||||
|
||||
func (f *RedisFactory) CreateCapesRepository() (http.CapesRepository, error) {
|
||||
panic("capes repository not supported for this storage type")
|
||||
}
|
||||
|
||||
func (f *RedisFactory) CreateMojangUuidsRepository() (mojangtextures.UuidsStorage, error) {
|
||||
return f.createInstance()
|
||||
}
|
||||
|
||||
func (f *RedisFactory) createInstance() (*redisDb, error) {
|
||||
p, err := f.getPool()
|
||||
func New(addr string, poolSize int) (*Redis, error) {
|
||||
conn, err := pool.New("tcp", addr, poolSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &redisDb{p}, nil
|
||||
return &Redis{
|
||||
pool: conn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *RedisFactory) getPool() (*pool.Pool, error) {
|
||||
if f.pool == nil {
|
||||
if f.Host == "" {
|
||||
return nil, &ParamRequired{"host"}
|
||||
}
|
||||
const accountIdToUsernameKey = "hash:username-to-account-id" // TODO: this should be actually "hash:user-id-to-username"
|
||||
const mojangUsernameToUuidKey = "hash:mojang-username-to-uuid"
|
||||
|
||||
if f.Port == 0 {
|
||||
return nil, &ParamRequired{"port"}
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", f.Host, f.Port)
|
||||
conn, err := pool.New("tcp", addr, f.PoolSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f.pool = conn
|
||||
}
|
||||
|
||||
return f.pool, nil
|
||||
}
|
||||
|
||||
type redisDb struct {
|
||||
type Redis struct {
|
||||
pool *pool.Pool
|
||||
}
|
||||
|
||||
const accountIdToUsernameKey = "hash:username-to-account-id"
|
||||
const mojangUsernameToUuidKey = "hash:mojang-username-to-uuid"
|
||||
|
||||
func (db *redisDb) FindByUsername(username string) (*model.Skin, error) {
|
||||
func (db *Redis) FindSkinByUsername(username string) (*model.Skin, error) {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -86,66 +47,6 @@ func (db *redisDb) FindByUsername(username string) (*model.Skin, error) {
|
||||
return findByUsername(username, conn)
|
||||
}
|
||||
|
||||
func (db *redisDb) FindByUserId(id int) (*model.Skin, error) {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return findByUserId(id, conn)
|
||||
}
|
||||
|
||||
func (db *redisDb) Save(skin *model.Skin) error {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return save(skin, conn)
|
||||
}
|
||||
|
||||
func (db *redisDb) RemoveByUserId(id int) error {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return removeByUserId(id, conn)
|
||||
}
|
||||
|
||||
func (db *redisDb) RemoveByUsername(username string) error {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return removeByUsername(username, conn)
|
||||
}
|
||||
|
||||
func (db *redisDb) GetUuid(username string) (string, error) {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return findMojangUuidByUsername(username, conn)
|
||||
}
|
||||
|
||||
func (db *redisDb) StoreUuid(username string, uuid string) error {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return storeMojangUuid(username, uuid, conn)
|
||||
}
|
||||
|
||||
func findByUsername(username string, conn util.Cmder) (*model.Skin, error) {
|
||||
redisKey := buildUsernameKey(username)
|
||||
response := conn.Cmd("GET", redisKey)
|
||||
@ -153,11 +54,7 @@ func findByUsername(username string, conn util.Cmder) (*model.Skin, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
encodedResult, err := response.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encodedResult, _ := response.Bytes()
|
||||
result, err := zlibDecode(encodedResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -174,6 +71,16 @@ func findByUsername(username string, conn util.Cmder) (*model.Skin, error) {
|
||||
return skin, nil
|
||||
}
|
||||
|
||||
func (db *Redis) FindSkinByUserId(id int) (*model.Skin, error) {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return findByUserId(id, conn)
|
||||
}
|
||||
|
||||
func findByUserId(id int, conn util.Cmder) (*model.Skin, error) {
|
||||
response := conn.Cmd("HGET", accountIdToUsernameKey, id)
|
||||
if response.IsType(redis.Nil) {
|
||||
@ -188,42 +95,14 @@ func findByUserId(id int, conn util.Cmder) (*model.Skin, error) {
|
||||
return findByUsername(username, conn)
|
||||
}
|
||||
|
||||
func removeByUserId(id int, conn util.Cmder) error {
|
||||
record, err := findByUserId(id, conn)
|
||||
func (db *Redis) SaveSkin(skin *model.Skin) error {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
conn.Cmd("MULTI")
|
||||
|
||||
conn.Cmd("HDEL", accountIdToUsernameKey, id)
|
||||
if record != nil {
|
||||
conn.Cmd("DEL", buildUsernameKey(record.Username))
|
||||
}
|
||||
|
||||
conn.Cmd("EXEC")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeByUsername(username string, conn util.Cmder) error {
|
||||
record, err := findByUsername(username, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
conn.Cmd("MULTI")
|
||||
|
||||
conn.Cmd("DEL", buildUsernameKey(record.Username))
|
||||
conn.Cmd("HDEL", accountIdToUsernameKey, record.UserId)
|
||||
|
||||
conn.Cmd("EXEC")
|
||||
|
||||
return nil
|
||||
return save(skin, conn)
|
||||
}
|
||||
|
||||
func save(skin *model.Skin, conn util.Cmder) error {
|
||||
@ -249,6 +128,74 @@ func save(skin *model.Skin, conn util.Cmder) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Redis) RemoveSkinByUserId(id int) error {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return removeByUserId(id, conn)
|
||||
}
|
||||
|
||||
func removeByUserId(id int, conn util.Cmder) error {
|
||||
record, err := findByUserId(id, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn.Cmd("MULTI")
|
||||
|
||||
conn.Cmd("HDEL", accountIdToUsernameKey, id)
|
||||
if record != nil {
|
||||
conn.Cmd("DEL", buildUsernameKey(record.Username))
|
||||
}
|
||||
|
||||
conn.Cmd("EXEC")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Redis) RemoveSkinByUsername(username string) error {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return removeByUsername(username, conn)
|
||||
}
|
||||
|
||||
func removeByUsername(username string, conn util.Cmder) error {
|
||||
record, err := findByUsername(username, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
conn.Cmd("MULTI")
|
||||
|
||||
conn.Cmd("DEL", buildUsernameKey(record.Username))
|
||||
conn.Cmd("HDEL", accountIdToUsernameKey, record.UserId)
|
||||
|
||||
conn.Cmd("EXEC")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Redis) GetUuid(username string) (string, error) {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return findMojangUuidByUsername(username, conn)
|
||||
}
|
||||
|
||||
func findMojangUuidByUsername(username string, conn util.Cmder) (string, error) {
|
||||
response := conn.Cmd("HGET", mojangUsernameToUuidKey, strings.ToLower(username))
|
||||
if response.IsType(redis.Nil) {
|
||||
@ -259,15 +206,25 @@ func findMojangUuidByUsername(username string, conn util.Cmder) (string, error)
|
||||
parts := strings.Split(data, ":")
|
||||
timestamp, _ := strconv.ParseInt(parts[1], 10, 64)
|
||||
storedAt := time.Unix(timestamp, 0)
|
||||
if storedAt.Add(time.Hour * 24 * 30).Before(time.Now()) {
|
||||
if storedAt.Add(time.Hour * 24 * 30).Before(now()) {
|
||||
return "", &mojangtextures.ValueNotFound{}
|
||||
}
|
||||
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
func (db *Redis) StoreUuid(username string, uuid string) error {
|
||||
conn, err := db.pool.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.pool.Put(conn)
|
||||
|
||||
return storeMojangUuid(username, uuid, conn)
|
||||
}
|
||||
|
||||
func storeMojangUuid(username string, uuid string, conn util.Cmder) error {
|
||||
value := uuid + ":" + strconv.FormatInt(time.Now().Unix(), 10)
|
||||
value := uuid + ":" + strconv.FormatInt(now().Unix(), 10)
|
||||
res := conn.Cmd("HSET", mojangUsernameToUuidKey, strings.ToLower(username), value)
|
||||
if res.IsType(redis.Err) {
|
||||
return res.Err
|
||||
@ -276,6 +233,15 @@ func storeMojangUuid(username string, uuid string, conn util.Cmder) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Redis) Ping() error {
|
||||
r := db.pool.Cmd("PING")
|
||||
if r.Err != nil {
|
||||
return r.Err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildUsernameKey(username string) string {
|
||||
return "username:" + strings.ToLower(username)
|
||||
}
|
||||
@ -291,14 +257,14 @@ func zlibEncode(str []byte) []byte {
|
||||
|
||||
func zlibDecode(bts []byte) ([]byte, error) {
|
||||
buff := bytes.NewReader(bts)
|
||||
reader, readError := zlib.NewReader(buff)
|
||||
if readError != nil {
|
||||
return nil, readError
|
||||
reader, err := zlib.NewReader(buff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultBuffer := new(bytes.Buffer)
|
||||
_, _ = io.Copy(resultBuffer, reader)
|
||||
reader.Close()
|
||||
_ = reader.Close()
|
||||
|
||||
return resultBuffer.Bytes(), nil
|
||||
}
|
361
db/redis/redis_integration_test.go
Normal file
361
db/redis/redis_integration_test.go
Normal file
@ -0,0 +1,361 @@
|
||||
// +build redis
|
||||
|
||||
package redis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mediocregopher/radix.v2/redis"
|
||||
assert "github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/elyby/chrly/model"
|
||||
"github.com/elyby/chrly/mojangtextures"
|
||||
)
|
||||
|
||||
const redisAddr = "localhost:6379"
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
t.Run("should connect", func(t *testing.T) {
|
||||
conn, err := New(redisAddr, 12)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, conn)
|
||||
internalPool := reflect.ValueOf(conn.pool).Elem().FieldByName("pool")
|
||||
assert.Equal(t, 12, internalPool.Cap())
|
||||
})
|
||||
|
||||
t.Run("should return error", func(t *testing.T) {
|
||||
conn, err := New("localhost:12345", 12) // Use localhost to avoid DNS resolution
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, conn)
|
||||
})
|
||||
}
|
||||
|
||||
type redisTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
Redis *Redis
|
||||
|
||||
cmd func(cmd string, args ...interface{}) *redis.Resp
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) SetupSuite() {
|
||||
conn, err := New(redisAddr, 10)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot establish connection to redis: %w", err))
|
||||
}
|
||||
|
||||
suite.Redis = conn
|
||||
suite.cmd = conn.pool.Cmd
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) SetupTest() {
|
||||
// Cleanup database before the each test
|
||||
suite.cmd("FLUSHALL")
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TearDownTest() {
|
||||
// Restore time.Now func
|
||||
now = time.Now
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) RunSubTest(name string, subTest func()) {
|
||||
suite.SetupTest()
|
||||
suite.Run(name, subTest)
|
||||
}
|
||||
|
||||
func TestRedis(t *testing.T) {
|
||||
suite.Run(t, new(redisTestSuite))
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON with zlib encoding
|
||||
* {
|
||||
* userId: 1,
|
||||
* uuid: "fd5da1e4d66d4d17aadee2446093896d",
|
||||
* username: "Mock",
|
||||
* skinId: 1,
|
||||
* url: "http://localhost/skin.png",
|
||||
* is1_8: true,
|
||||
* isSlim: false,
|
||||
* mojangTextures: "mock-mojang-textures",
|
||||
* mojangSignature: "mock-mojang-signature"
|
||||
* }
|
||||
*/
|
||||
var skinRecord = []byte{
|
||||
0x78, 0x9c, 0x5c, 0xce, 0x4b, 0x4a, 0x4, 0x41, 0xc, 0xc6, 0xf1, 0xbb, 0x7c, 0xeb, 0x1a, 0xdb, 0xd6, 0xb2,
|
||||
0x9c, 0xc9, 0xd, 0x5c, 0x88, 0x8b, 0xd1, 0xb5, 0x84, 0x4e, 0xa6, 0xa7, 0xec, 0x7a, 0xc, 0xf5, 0x0, 0x41,
|
||||
0xbc, 0xbb, 0xb4, 0xd2, 0xa, 0x2e, 0xf3, 0xe3, 0x9f, 0x90, 0xf, 0xf4, 0xaa, 0xe5, 0x41, 0x40, 0xa3, 0x41,
|
||||
0xef, 0x5e, 0x40, 0x38, 0xc9, 0x9d, 0xf0, 0xa8, 0x56, 0x9c, 0x13, 0x2b, 0xe3, 0x3d, 0xb3, 0xa8, 0xde, 0x58,
|
||||
0xeb, 0xae, 0xf, 0xb7, 0xfb, 0x83, 0x13, 0x98, 0xef, 0xa5, 0xc4, 0x51, 0x41, 0x78, 0xcc, 0xd3, 0x2, 0x83,
|
||||
0xba, 0xf8, 0xb4, 0x9d, 0x29, 0x1, 0x84, 0x73, 0x6b, 0x17, 0x1a, 0x86, 0x90, 0x27, 0xe, 0xe7, 0x5c, 0xdb,
|
||||
0xb0, 0x16, 0x57, 0x97, 0x34, 0xc3, 0xc0, 0xd7, 0xf1, 0x75, 0xf, 0x6a, 0xa5, 0xeb, 0x3a, 0x1c, 0x83, 0x8f,
|
||||
0xa0, 0x13, 0x87, 0xaa, 0x6, 0x31, 0xbf, 0x71, 0x9a, 0x9f, 0xf5, 0xbd, 0xf5, 0xa2, 0x15, 0x84, 0x98, 0xa7,
|
||||
0x65, 0xf7, 0xa3, 0xbb, 0xb6, 0xf1, 0xd6, 0x1d, 0xfd, 0x9c, 0x78, 0xa5, 0x7f, 0x61, 0xfd, 0x75, 0x83, 0xa7,
|
||||
0x20, 0x2f, 0x7f, 0xff, 0xe2, 0xf3, 0x2b, 0x0, 0x0, 0xff, 0xff, 0x6f, 0xdd, 0x51, 0x71,
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestFindSkinByUsername() {
|
||||
suite.RunSubTest("exists record", func() {
|
||||
suite.cmd("SET", "username:mock", skinRecord)
|
||||
|
||||
skin, err := suite.Redis.FindSkinByUsername("Mock")
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().NotNil(skin)
|
||||
suite.Require().Equal(1, skin.UserId)
|
||||
suite.Require().Equal("fd5da1e4d66d4d17aadee2446093896d", skin.Uuid)
|
||||
suite.Require().Equal("Mock", skin.Username)
|
||||
suite.Require().Equal(1, skin.SkinId)
|
||||
suite.Require().Equal("http://localhost/skin.png", skin.Url)
|
||||
suite.Require().True(skin.Is1_8)
|
||||
suite.Require().False(skin.IsSlim)
|
||||
suite.Require().Equal("mock-mojang-textures", skin.MojangTextures)
|
||||
suite.Require().Equal("mock-mojang-signature", skin.MojangSignature)
|
||||
suite.Require().Equal(skin.Username, skin.OldUsername)
|
||||
})
|
||||
|
||||
suite.RunSubTest("not exists record", func() {
|
||||
skin, err := suite.Redis.FindSkinByUsername("Mock")
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().Nil(skin)
|
||||
})
|
||||
|
||||
suite.RunSubTest("invalid zlib encoding", func() {
|
||||
suite.cmd("SET", "username:mock", "this is really not zlib")
|
||||
skin, err := suite.Redis.FindSkinByUsername("Mock")
|
||||
suite.Require().Nil(skin)
|
||||
suite.Require().EqualError(err, "zlib: invalid header")
|
||||
})
|
||||
|
||||
suite.RunSubTest("invalid json encoding", func() {
|
||||
suite.cmd("SET", "username:mock", []byte{
|
||||
0x78, 0x9c, 0xca, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0x1, 0x4, 0x0, 0x0, 0xff,
|
||||
0xff, 0x1a, 0xb, 0x4, 0x5d,
|
||||
})
|
||||
skin, err := suite.Redis.FindSkinByUsername("Mock")
|
||||
suite.Require().Nil(skin)
|
||||
suite.Require().EqualError(err, "invalid character 'h' looking for beginning of value")
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestFindSkinByUserId() {
|
||||
suite.RunSubTest("exists record", func() {
|
||||
suite.cmd("SET", "username:mock", skinRecord)
|
||||
suite.cmd("HSET", "hash:username-to-account-id", 1, "Mock")
|
||||
|
||||
skin, err := suite.Redis.FindSkinByUserId(1)
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().NotNil(skin)
|
||||
suite.Require().Equal(1, skin.UserId)
|
||||
})
|
||||
|
||||
suite.RunSubTest("not exists record", func() {
|
||||
skin, err := suite.Redis.FindSkinByUserId(1)
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().Nil(skin)
|
||||
})
|
||||
|
||||
suite.RunSubTest("exists hash record, but no skin record", func() {
|
||||
suite.cmd("HSET", "hash:username-to-account-id", 1, "Mock")
|
||||
skin, err := suite.Redis.FindSkinByUserId(1)
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().Nil(skin)
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestSaveSkin() {
|
||||
suite.RunSubTest("save new entity", func() {
|
||||
err := suite.Redis.SaveSkin(&model.Skin{
|
||||
UserId: 1,
|
||||
Uuid: "fd5da1e4d66d4d17aadee2446093896d",
|
||||
Username: "Mock",
|
||||
SkinId: 1,
|
||||
Url: "http://localhost/skin.png",
|
||||
Is1_8: true,
|
||||
IsSlim: false,
|
||||
MojangTextures: "mock-mojang-textures",
|
||||
MojangSignature: "mock-mojang-signature",
|
||||
})
|
||||
suite.Require().Nil(err)
|
||||
|
||||
usernameResp := suite.cmd("GET", "username:mock")
|
||||
suite.Require().False(usernameResp.IsType(redis.Nil))
|
||||
bytes, _ := usernameResp.Bytes()
|
||||
suite.Require().Equal(skinRecord, bytes)
|
||||
|
||||
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
|
||||
suite.Require().False(usernameResp.IsType(redis.Nil))
|
||||
str, _ := idResp.Str()
|
||||
suite.Require().Equal("Mock", str)
|
||||
})
|
||||
|
||||
suite.RunSubTest("save exists record with changed username", func() {
|
||||
suite.cmd("SET", "username:mock", skinRecord)
|
||||
suite.cmd("HSET", "hash:username-to-account-id", 1, "Mock")
|
||||
|
||||
err := suite.Redis.SaveSkin(&model.Skin{
|
||||
UserId: 1,
|
||||
Uuid: "fd5da1e4d66d4d17aadee2446093896d",
|
||||
Username: "NewMock",
|
||||
SkinId: 1,
|
||||
Url: "http://localhost/skin.png",
|
||||
Is1_8: true,
|
||||
IsSlim: false,
|
||||
MojangTextures: "mock-mojang-textures",
|
||||
MojangSignature: "mock-mojang-signature",
|
||||
OldUsername: "Mock",
|
||||
})
|
||||
suite.Require().Nil(err)
|
||||
|
||||
usernameResp := suite.cmd("GET", "username:newmock")
|
||||
suite.Require().False(usernameResp.IsType(redis.Nil))
|
||||
bytes, _ := usernameResp.Bytes()
|
||||
suite.Require().Equal([]byte{
|
||||
0x78, 0x9c, 0x5c, 0x8e, 0xcb, 0x4e, 0xc3, 0x40, 0xc, 0x45, 0xff, 0xe5, 0xae, 0xa7, 0x84, 0x40, 0x18, 0x5a,
|
||||
0xff, 0x1, 0xb, 0x60, 0x51, 0x58, 0x23, 0x2b, 0x76, 0xd3, 0x21, 0xf3, 0xa8, 0xe6, 0x21, 0x90, 0x10, 0xff,
|
||||
0x8e, 0x52, 0x14, 0x90, 0xba, 0xf4, 0xd1, 0xf1, 0xd5, 0xf9, 0x42, 0x2b, 0x9a, 0x1f, 0x4, 0xd4, 0x1b, 0xb4,
|
||||
0xe6, 0x4, 0x84, 0x83, 0xdc, 0x9, 0xf7, 0x3a, 0x88, 0xb5, 0x32, 0x48, 0x7f, 0xcf, 0x2c, 0xaa, 0x37, 0xc3,
|
||||
0x60, 0xaf, 0x77, 0xb7, 0xdb, 0x9d, 0x15, 0x98, 0xf3, 0x53, 0xe4, 0xa0, 0x20, 0x3c, 0xe9, 0xc7, 0x63, 0x1a,
|
||||
0x67, 0x18, 0x94, 0xd9, 0xc5, 0x75, 0x29, 0x7b, 0x10, 0x8e, 0xb5, 0x9e, 0xa8, 0xeb, 0x7c, 0x1a, 0xd9, 0x1f,
|
||||
0x53, 0xa9, 0xdd, 0x62, 0x5c, 0x9d, 0xe2, 0x4, 0x3, 0x57, 0xfa, 0xb7, 0x2d, 0xa8, 0xe6, 0xa6, 0xcb, 0xb1,
|
||||
0xf7, 0x2e, 0x80, 0xe, 0xec, 0x8b, 0x1a, 0x84, 0xf4, 0xce, 0x71, 0x7a, 0xd1, 0xcf, 0xda, 0xb2, 0x16, 0x10,
|
||||
0x42, 0x1a, 0xe7, 0xcd, 0x2f, 0xdd, 0xd4, 0x15, 0xaf, 0xde, 0xde, 0x4d, 0x91, 0x17, 0x74, 0x21, 0x96, 0x3f,
|
||||
0x6e, 0xf0, 0xec, 0xe5, 0xf5, 0x3f, 0xf9, 0xdc, 0xfb, 0xfd, 0x13, 0x0, 0x0, 0xff, 0xff, 0xca, 0xc3, 0x54,
|
||||
0x25,
|
||||
}, bytes)
|
||||
|
||||
oldUsernameResp := suite.cmd("GET", "username:mock")
|
||||
suite.Require().True(oldUsernameResp.IsType(redis.Nil))
|
||||
|
||||
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
|
||||
suite.Require().False(usernameResp.IsType(redis.Nil))
|
||||
str, _ := idResp.Str()
|
||||
suite.Require().Equal("NewMock", str)
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestRemoveSkinByUserId() {
|
||||
suite.RunSubTest("exists record", func() {
|
||||
suite.cmd("SET", "username:mock", skinRecord)
|
||||
suite.cmd("HSET", "hash:username-to-account-id", 1, "Mock")
|
||||
|
||||
err := suite.Redis.RemoveSkinByUserId(1)
|
||||
suite.Require().Nil(err)
|
||||
|
||||
usernameResp := suite.cmd("GET", "username:mock")
|
||||
suite.Require().True(usernameResp.IsType(redis.Nil))
|
||||
|
||||
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
|
||||
suite.Require().True(idResp.IsType(redis.Nil))
|
||||
})
|
||||
|
||||
suite.RunSubTest("exists only id", func() {
|
||||
suite.cmd("HSET", "hash:username-to-account-id", 1, "Mock")
|
||||
|
||||
err := suite.Redis.RemoveSkinByUserId(1)
|
||||
suite.Require().Nil(err)
|
||||
|
||||
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
|
||||
suite.Require().True(idResp.IsType(redis.Nil))
|
||||
})
|
||||
|
||||
suite.RunSubTest("error when querying skin record", func() {
|
||||
suite.cmd("SET", "username:mock", "invalid zlib")
|
||||
suite.cmd("HSET", "hash:username-to-account-id", 1, "Mock")
|
||||
|
||||
err := suite.Redis.RemoveSkinByUserId(1)
|
||||
suite.Require().EqualError(err, "zlib: invalid header")
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestRemoveSkinByUsername() {
|
||||
suite.RunSubTest("exists record", func() {
|
||||
suite.cmd("SET", "username:mock", skinRecord)
|
||||
suite.cmd("HSET", "hash:username-to-account-id", 1, "Mock")
|
||||
|
||||
err := suite.Redis.RemoveSkinByUsername("Mock")
|
||||
suite.Require().Nil(err)
|
||||
|
||||
usernameResp := suite.cmd("GET", "username:mock")
|
||||
suite.Require().True(usernameResp.IsType(redis.Nil))
|
||||
|
||||
idResp := suite.cmd("HGET", "hash:username-to-account-id", 1)
|
||||
suite.Require().True(idResp.IsType(redis.Nil))
|
||||
})
|
||||
|
||||
suite.RunSubTest("exists only username", func() {
|
||||
suite.cmd("SET", "username:mock", skinRecord)
|
||||
|
||||
err := suite.Redis.RemoveSkinByUsername("Mock")
|
||||
suite.Require().Nil(err)
|
||||
|
||||
usernameResp := suite.cmd("GET", "username:mock")
|
||||
suite.Require().True(usernameResp.IsType(redis.Nil))
|
||||
})
|
||||
|
||||
suite.RunSubTest("no records", func() {
|
||||
err := suite.Redis.RemoveSkinByUsername("Mock")
|
||||
suite.Require().Nil(err)
|
||||
})
|
||||
|
||||
suite.RunSubTest("error when querying skin record", func() {
|
||||
suite.cmd("SET", "username:mock", "invalid zlib")
|
||||
|
||||
err := suite.Redis.RemoveSkinByUsername("Mock")
|
||||
suite.Require().EqualError(err, "zlib: invalid header")
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestGetUuid() {
|
||||
suite.RunSubTest("exists record", func() {
|
||||
suite.cmd("HSET",
|
||||
"hash:mojang-username-to-uuid",
|
||||
"mock",
|
||||
fmt.Sprintf("%s:%d", "d3ca513eb3e14946b58047f2bd3530fd", time.Now().Unix()),
|
||||
)
|
||||
|
||||
uuid, err := suite.Redis.GetUuid("Mock")
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().Equal("d3ca513eb3e14946b58047f2bd3530fd", uuid)
|
||||
})
|
||||
|
||||
suite.RunSubTest("not exists record", func() {
|
||||
uuid, err := suite.Redis.GetUuid("Mock")
|
||||
suite.Require().Empty(uuid)
|
||||
suite.Require().IsType(new(mojangtextures.ValueNotFound), err)
|
||||
})
|
||||
|
||||
suite.RunSubTest("exists, but expired record", func() {
|
||||
suite.cmd("HSET",
|
||||
"hash:mojang-username-to-uuid",
|
||||
"mock",
|
||||
fmt.Sprintf("%s:%d", "d3ca513eb3e14946b58047f2bd3530fd", time.Now().Add(-1*time.Hour*24*31).Unix()),
|
||||
)
|
||||
|
||||
uuid, err := suite.Redis.GetUuid("Mock")
|
||||
suite.Require().Empty(uuid)
|
||||
suite.Require().IsType(new(mojangtextures.ValueNotFound), err)
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestStoreUuid() {
|
||||
now = func() time.Time {
|
||||
return time.Date(2020, 04, 21, 02, 10, 16, 0, time.UTC)
|
||||
}
|
||||
|
||||
err := suite.Redis.StoreUuid("Mock", "d3ca513eb3e14946b58047f2bd3530fd")
|
||||
suite.Require().Nil(err)
|
||||
|
||||
resp := suite.cmd("HGET", "hash:mojang-username-to-uuid", "mock")
|
||||
suite.Require().False(resp.IsType(redis.Nil))
|
||||
str, _ := resp.Str()
|
||||
suite.Require().Equal(str, "d3ca513eb3e14946b58047f2bd3530fd:1587435016")
|
||||
}
|
||||
|
||||
func (suite *redisTestSuite) TestPing() {
|
||||
err := suite.Redis.Ping()
|
||||
suite.Require().Nil(err)
|
||||
}
|
85
di/db.go
85
di/db.go
@ -1,61 +1,68 @@
|
||||
package di
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/goava/di"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
. "github.com/elyby/chrly/db"
|
||||
"github.com/elyby/chrly/db/fs"
|
||||
"github.com/elyby/chrly/db/redis"
|
||||
es "github.com/elyby/chrly/eventsubscribers"
|
||||
"github.com/elyby/chrly/http"
|
||||
"github.com/elyby/chrly/mojangtextures"
|
||||
)
|
||||
|
||||
var db = di.Options(
|
||||
di.Provide(newRedisFactory),
|
||||
di.Provide(newFSFactory),
|
||||
di.Provide(newSkinsRepository),
|
||||
di.Provide(newCapesRepository),
|
||||
di.Provide(newMojangUUIDsRepository),
|
||||
di.Provide(newMojangSignedTexturesStorage),
|
||||
)
|
||||
|
||||
func newRedisFactory(config *viper.Viper) *RedisFactory {
|
||||
config.SetDefault("storage.redis.host", "localhost")
|
||||
config.SetDefault("storage.redis.port", 6379)
|
||||
config.SetDefault("storage.redis.poll", 10)
|
||||
|
||||
return &RedisFactory{
|
||||
Host: config.GetString("storage.redis.host"),
|
||||
Port: config.GetInt("storage.redis.port"),
|
||||
PoolSize: config.GetInt("storage.redis.poolSize"),
|
||||
}
|
||||
}
|
||||
|
||||
func newFSFactory(config *viper.Viper) *FilesystemFactory {
|
||||
config.SetDefault("storage.filesystem.basePath", "data")
|
||||
config.SetDefault("storage.filesystem.capesDirName", "capes")
|
||||
|
||||
return &FilesystemFactory{
|
||||
BasePath: config.GetString("storage.filesystem.basePath"),
|
||||
CapesDirName: config.GetString("storage.filesystem.capesDirName"),
|
||||
}
|
||||
}
|
||||
|
||||
// v4 had the idea that it would be possible to separate backends for storing skins and capes.
|
||||
// But in v5 the storage will be unified, so this is just temporary constructors before large reworking.
|
||||
//
|
||||
// Since there are no options for selecting target backends,
|
||||
// all constants in this case point to static specific implementations.
|
||||
var db = di.Options(
|
||||
di.Provide(newRedis,
|
||||
di.As(new(http.SkinsRepository)),
|
||||
di.As(new(mojangtextures.UuidsStorage)),
|
||||
),
|
||||
di.Provide(newFSFactory,
|
||||
di.As(new(http.CapesRepository)),
|
||||
),
|
||||
di.Provide(newMojangSignedTexturesStorage),
|
||||
)
|
||||
|
||||
func newSkinsRepository(factory *RedisFactory) (http.SkinsRepository, error) {
|
||||
return factory.CreateSkinsRepository()
|
||||
func newRedis(container *di.Container, config *viper.Viper) (*redis.Redis, error) {
|
||||
config.SetDefault("storage.redis.host", "localhost")
|
||||
config.SetDefault("storage.redis.port", 6379)
|
||||
config.SetDefault("storage.redis.poll", 10)
|
||||
|
||||
conn, err := redis.New(
|
||||
fmt.Sprintf("%s:%d", config.GetString("storage.redis.host"), config.GetInt("storage.redis.port")),
|
||||
config.GetInt("storage.redis.poolSize"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := container.Provide(func() *namedHealthChecker {
|
||||
return &namedHealthChecker{
|
||||
Name: "redis",
|
||||
Checker: es.DatabaseChecker(conn),
|
||||
}
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func newCapesRepository(factory *FilesystemFactory) (http.CapesRepository, error) {
|
||||
return factory.CreateCapesRepository()
|
||||
}
|
||||
func newFSFactory(config *viper.Viper) (*fs.Filesystem, error) {
|
||||
config.SetDefault("storage.filesystem.basePath", "data")
|
||||
config.SetDefault("storage.filesystem.capesDirName", "capes")
|
||||
|
||||
func newMojangUUIDsRepository(factory *RedisFactory) (mojangtextures.UuidsStorage, error) {
|
||||
return factory.CreateMojangUuidsRepository()
|
||||
return fs.New(path.Join(
|
||||
config.GetString("storage.filesystem.basePath"),
|
||||
config.GetString("storage.filesystem.capesDirName"),
|
||||
))
|
||||
}
|
||||
|
||||
func newMojangSignedTexturesStorage() mojangtextures.TexturesStorage {
|
||||
|
@ -87,7 +87,7 @@ func newHandlerFactory(
|
||||
checkersOptions[i] = healthcheck.WithChecker(checker.Name, checker.Checker)
|
||||
}
|
||||
|
||||
router.Handle("/healthcheck", healthcheck.Handler()).Methods("GET")
|
||||
router.Handle("/healthcheck", healthcheck.Handler(checkersOptions...)).Methods("GET")
|
||||
}
|
||||
|
||||
return router, nil
|
||||
|
@ -11,6 +11,26 @@ import (
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
)
|
||||
|
||||
type Pingable interface {
|
||||
Ping() error
|
||||
}
|
||||
|
||||
func DatabaseChecker(connection Pingable) healthcheck.CheckerFunc {
|
||||
return func(ctx context.Context) error {
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
done <- connection.Ping()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.New("check timeout")
|
||||
case err := <-done:
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func MojangBatchUuidsProviderResponseChecker(dispatcher Subscriber, resetDuration time.Duration) healthcheck.CheckerFunc {
|
||||
var mutex sync.Mutex
|
||||
var lastCallErr error
|
||||
|
@ -7,18 +7,56 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/elyby/chrly/api/mojang"
|
||||
"github.com/elyby/chrly/dispatcher"
|
||||
)
|
||||
|
||||
type pingableMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (p *pingableMock) Ping() error {
|
||||
args := p.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func TestDatabaseChecker(t *testing.T) {
|
||||
t.Run("no error", func(t *testing.T) {
|
||||
p := &pingableMock{}
|
||||
p.On("Ping").Return(nil)
|
||||
checker := DatabaseChecker(p)
|
||||
assert.Nil(t, checker(context.Background()))
|
||||
})
|
||||
|
||||
t.Run("with error", func(t *testing.T) {
|
||||
err := errors.New("mock error")
|
||||
p := &pingableMock{}
|
||||
p.On("Ping").Return(err)
|
||||
checker := DatabaseChecker(p)
|
||||
assert.Equal(t, err, checker(context.Background()))
|
||||
})
|
||||
|
||||
t.Run("context timeout", func(t *testing.T) {
|
||||
p := &pingableMock{}
|
||||
waitChan := make(chan time.Time, 1)
|
||||
p.On("Ping").WaitUntil(waitChan).Return(nil)
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), 0)
|
||||
checker := DatabaseChecker(p)
|
||||
assert.Errorf(t, checker(ctx), "check timeout")
|
||||
close(waitChan)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMojangBatchUuidsProviderChecker(t *testing.T) {
|
||||
t.Run("empty state", func(t *testing.T) {
|
||||
d := dispatcher.New()
|
||||
checker := MojangBatchUuidsProviderResponseChecker(d, time.Millisecond)
|
||||
assert.Nil(t, checker(context.Background()))
|
||||
})
|
||||
|
||||
//
|
||||
t.Run("when no error occurred", func(t *testing.T) {
|
||||
d := dispatcher.New()
|
||||
checker := MojangBatchUuidsProviderResponseChecker(d, time.Millisecond)
|
||||
|
16
http/api.go
16
http/api.go
@ -92,7 +92,7 @@ func (ctx *Api) postSkinHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
record.MojangTextures = req.Form.Get("mojangTextures")
|
||||
record.MojangSignature = req.Form.Get("mojangSignature")
|
||||
|
||||
err = ctx.SkinsRepo.Save(record)
|
||||
err = ctx.SkinsRepo.SaveSkin(record)
|
||||
if err != nil {
|
||||
ctx.Emit("skinsystem:error", fmt.Errorf("unable to save record to the repository: %w", err))
|
||||
apiServerError(resp)
|
||||
@ -104,13 +104,13 @@ func (ctx *Api) postSkinHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
|
||||
func (ctx *Api) deleteSkinByUserIdHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
id, _ := strconv.Atoi(mux.Vars(req)["id"])
|
||||
skin, err := ctx.SkinsRepo.FindByUserId(id)
|
||||
skin, err := ctx.SkinsRepo.FindSkinByUserId(id)
|
||||
ctx.deleteSkin(skin, err, resp)
|
||||
}
|
||||
|
||||
func (ctx *Api) deleteSkinByUsernameHandler(resp http.ResponseWriter, req *http.Request) {
|
||||
username := mux.Vars(req)["username"]
|
||||
skin, err := ctx.SkinsRepo.FindByUsername(username)
|
||||
skin, err := ctx.SkinsRepo.FindSkinByUsername(username)
|
||||
ctx.deleteSkin(skin, err, resp)
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ func (ctx *Api) deleteSkin(skin *model.Skin, err error, resp http.ResponseWriter
|
||||
return
|
||||
}
|
||||
|
||||
err = ctx.SkinsRepo.RemoveByUserId(skin.UserId)
|
||||
err = ctx.SkinsRepo.RemoveSkinByUserId(skin.UserId)
|
||||
if err != nil {
|
||||
ctx.Emit("skinsystem:error", fmt.Errorf("cannot delete skin by error: %w", err))
|
||||
apiServerError(resp)
|
||||
@ -137,7 +137,7 @@ func (ctx *Api) deleteSkin(skin *model.Skin, err error, resp http.ResponseWriter
|
||||
}
|
||||
|
||||
func (ctx *Api) findIdentityOrCleanup(identityId int, username string) (*model.Skin, error) {
|
||||
record, err := ctx.SkinsRepo.FindByUserId(identityId)
|
||||
record, err := ctx.SkinsRepo.FindSkinByUserId(identityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -146,7 +146,7 @@ func (ctx *Api) findIdentityOrCleanup(identityId int, username string) (*model.S
|
||||
// The username may have changed in the external database,
|
||||
// so we need to remove the old association
|
||||
if record.Username != username {
|
||||
_ = ctx.SkinsRepo.RemoveByUserId(identityId)
|
||||
_ = ctx.SkinsRepo.RemoveSkinByUserId(identityId)
|
||||
record.Username = username
|
||||
}
|
||||
|
||||
@ -155,14 +155,14 @@ func (ctx *Api) findIdentityOrCleanup(identityId int, username string) (*model.S
|
||||
|
||||
// If the requested id was not found, then username was reassigned to another user
|
||||
// who has not uploaded his data to Chrly yet
|
||||
record, err = ctx.SkinsRepo.FindByUsername(username)
|
||||
record, err = ctx.SkinsRepo.FindSkinByUsername(username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the target username does exist, clear it as it will be reassigned to the new user
|
||||
if record != nil {
|
||||
_ = ctx.SkinsRepo.RemoveByUsername(username)
|
||||
_ = ctx.SkinsRepo.RemoveSkinByUsername(username)
|
||||
record.UserId = identityId
|
||||
|
||||
return record, nil
|
||||
|
@ -88,9 +88,9 @@ var postSkinTestsCases = []*postSkinTestCase{
|
||||
"url": {"http://example.com/skin.png"},
|
||||
}.Encode()),
|
||||
BeforeTest: func(suite *apiTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUserId", 1).Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("Save", mock.MatchedBy(func(model *model.Skin) bool {
|
||||
suite.SkinsRepository.On("FindSkinByUserId", 1).Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("SaveSkin", mock.MatchedBy(func(model *model.Skin) bool {
|
||||
suite.Equal(1, model.UserId)
|
||||
suite.Equal("mock_username", model.Username)
|
||||
suite.Equal("0f657aa8-bfbe-415d-b700-5750090d3af3", model.Uuid)
|
||||
@ -120,8 +120,8 @@ var postSkinTestsCases = []*postSkinTestCase{
|
||||
"url": {"http://textures-server.com/skin.png"},
|
||||
}.Encode()),
|
||||
BeforeTest: func(suite *apiTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUserId", 1).Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("Save", mock.MatchedBy(func(model *model.Skin) bool {
|
||||
suite.SkinsRepository.On("FindSkinByUserId", 1).Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("SaveSkin", mock.MatchedBy(func(model *model.Skin) bool {
|
||||
suite.Equal(1, model.UserId)
|
||||
suite.Equal("mock_username", model.Username)
|
||||
suite.Equal("0f657aa8-bfbe-415d-b700-5750090d3af3", model.Uuid)
|
||||
@ -151,10 +151,10 @@ var postSkinTestsCases = []*postSkinTestCase{
|
||||
"url": {"http://example.com/skin.png"},
|
||||
}.Encode()),
|
||||
BeforeTest: func(suite *apiTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUserId", 2).Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("RemoveByUsername", "mock_username").Times(1).Return(nil)
|
||||
suite.SkinsRepository.On("Save", mock.MatchedBy(func(model *model.Skin) bool {
|
||||
suite.SkinsRepository.On("FindSkinByUserId", 2).Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("RemoveSkinByUsername", "mock_username").Times(1).Return(nil)
|
||||
suite.SkinsRepository.On("SaveSkin", mock.MatchedBy(func(model *model.Skin) bool {
|
||||
suite.Equal(2, model.UserId)
|
||||
suite.Equal("mock_username", model.Username)
|
||||
suite.Equal("0f657aa8-bfbe-415d-b700-5750090d3af3", model.Uuid)
|
||||
@ -180,9 +180,9 @@ var postSkinTestsCases = []*postSkinTestCase{
|
||||
"url": {"http://example.com/skin.png"},
|
||||
}.Encode()),
|
||||
BeforeTest: func(suite *apiTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUserId", 1).Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("RemoveByUserId", 1).Times(1).Return(nil)
|
||||
suite.SkinsRepository.On("Save", mock.MatchedBy(func(model *model.Skin) bool {
|
||||
suite.SkinsRepository.On("FindSkinByUserId", 1).Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("RemoveSkinByUserId", 1).Times(1).Return(nil)
|
||||
suite.SkinsRepository.On("SaveSkin", mock.MatchedBy(func(model *model.Skin) bool {
|
||||
suite.Equal(1, model.UserId)
|
||||
suite.Equal("changed_username", model.Username)
|
||||
suite.Equal("0f657aa8-bfbe-415d-b700-5750090d3af3", model.Uuid)
|
||||
@ -208,9 +208,9 @@ var postSkinTestsCases = []*postSkinTestCase{
|
||||
"url": {"http://textures-server.com/skin.png"},
|
||||
}.Encode()),
|
||||
BeforeTest: func(suite *apiTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUserId", 1).Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("FindSkinByUserId", 1).Return(createSkinModel("mock_username", false), nil)
|
||||
err := errors.New("mock error")
|
||||
suite.SkinsRepository.On("Save", mock.Anything).Return(err)
|
||||
suite.SkinsRepository.On("SaveSkin", mock.Anything).Return(err)
|
||||
suite.Emitter.On("Emit", "skinsystem:error", mock.MatchedBy(func(cErr error) bool {
|
||||
return cErr.Error() == "unable to save record to the repository: mock error" &&
|
||||
errors.Is(cErr, err)
|
||||
@ -235,7 +235,7 @@ var postSkinTestsCases = []*postSkinTestCase{
|
||||
}.Encode()),
|
||||
BeforeTest: func(suite *apiTestSuite) {
|
||||
err := errors.New("mock error")
|
||||
suite.SkinsRepository.On("FindByUserId", 1).Return(nil, err)
|
||||
suite.SkinsRepository.On("FindSkinByUserId", 1).Return(nil, err)
|
||||
suite.Emitter.On("Emit", "skinsystem:error", mock.MatchedBy(func(cErr error) bool {
|
||||
return cErr.Error() == "error on requesting a skin from the repository: mock error" &&
|
||||
errors.Is(cErr, err)
|
||||
@ -352,8 +352,8 @@ func (suite *apiTestSuite) TestPostSkin() {
|
||||
|
||||
func (suite *apiTestSuite) TestDeleteByUserId() {
|
||||
suite.RunSubTest("Delete skin by its identity id", func() {
|
||||
suite.SkinsRepository.On("FindByUserId", 1).Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("RemoveByUserId", 1).Once().Return(nil)
|
||||
suite.SkinsRepository.On("FindSkinByUserId", 1).Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("RemoveSkinByUserId", 1).Once().Return(nil)
|
||||
|
||||
req := httptest.NewRequest("DELETE", "http://chrly/skins/id:1", nil)
|
||||
w := httptest.NewRecorder()
|
||||
@ -368,7 +368,7 @@ func (suite *apiTestSuite) TestDeleteByUserId() {
|
||||
})
|
||||
|
||||
suite.RunSubTest("Try to remove not exists identity id", func() {
|
||||
suite.SkinsRepository.On("FindByUserId", 1).Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUserId", 1).Return(nil, nil)
|
||||
|
||||
req := httptest.NewRequest("DELETE", "http://chrly/skins/id:1", nil)
|
||||
w := httptest.NewRecorder()
|
||||
@ -391,8 +391,8 @@ func (suite *apiTestSuite) TestDeleteByUserId() {
|
||||
|
||||
func (suite *apiTestSuite) TestDeleteByUsername() {
|
||||
suite.RunSubTest("Delete skin by its identity username", func() {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("RemoveByUserId", 1).Once().Return(nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("RemoveSkinByUserId", 1).Once().Return(nil)
|
||||
|
||||
req := httptest.NewRequest("DELETE", "http://chrly/skins/mock_username", nil)
|
||||
w := httptest.NewRecorder()
|
||||
@ -407,7 +407,7 @@ func (suite *apiTestSuite) TestDeleteByUsername() {
|
||||
})
|
||||
|
||||
suite.RunSubTest("Try to remove not exists identity username", func() {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
|
||||
req := httptest.NewRequest("DELETE", "http://chrly/skins/mock_username", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
@ -14,15 +14,15 @@ import (
|
||||
)
|
||||
|
||||
type SkinsRepository interface {
|
||||
FindByUsername(username string) (*model.Skin, error)
|
||||
FindByUserId(id int) (*model.Skin, error)
|
||||
Save(skin *model.Skin) error
|
||||
RemoveByUserId(id int) error
|
||||
RemoveByUsername(username string) error
|
||||
FindSkinByUsername(username string) (*model.Skin, error)
|
||||
FindSkinByUserId(id int) (*model.Skin, error)
|
||||
SaveSkin(skin *model.Skin) error
|
||||
RemoveSkinByUserId(id int) error
|
||||
RemoveSkinByUsername(username string) error
|
||||
}
|
||||
|
||||
type CapesRepository interface {
|
||||
FindByUsername(username string) (*model.Cape, error)
|
||||
FindCapeByUsername(username string) (*model.Cape, error)
|
||||
}
|
||||
|
||||
type MojangTexturesProvider interface {
|
||||
@ -54,7 +54,7 @@ func (ctx *Skinsystem) Handler() *mux.Router {
|
||||
|
||||
func (ctx *Skinsystem) skinHandler(response http.ResponseWriter, request *http.Request) {
|
||||
username := parseUsername(mux.Vars(request)["username"])
|
||||
rec, err := ctx.SkinsRepo.FindByUsername(username)
|
||||
rec, err := ctx.SkinsRepo.FindSkinByUsername(username)
|
||||
if err == nil && rec != nil && rec.SkinId != 0 {
|
||||
http.Redirect(response, request, rec.Url, 301)
|
||||
return
|
||||
@ -91,7 +91,7 @@ func (ctx *Skinsystem) skinGetHandler(response http.ResponseWriter, request *htt
|
||||
|
||||
func (ctx *Skinsystem) capeHandler(response http.ResponseWriter, request *http.Request) {
|
||||
username := parseUsername(mux.Vars(request)["username"])
|
||||
rec, err := ctx.CapesRepo.FindByUsername(username)
|
||||
rec, err := ctx.CapesRepo.FindCapeByUsername(username)
|
||||
if err == nil && rec != nil {
|
||||
request.Header.Set("Content-Type", "image/png")
|
||||
_, _ = io.Copy(response, rec.File)
|
||||
@ -131,8 +131,8 @@ func (ctx *Skinsystem) texturesHandler(response http.ResponseWriter, request *ht
|
||||
username := parseUsername(mux.Vars(request)["username"])
|
||||
|
||||
var textures *mojang.TexturesResponse
|
||||
skin, skinErr := ctx.SkinsRepo.FindByUsername(username)
|
||||
cape, capeErr := ctx.CapesRepo.FindByUsername(username)
|
||||
skin, skinErr := ctx.SkinsRepo.FindSkinByUsername(username)
|
||||
cape, capeErr := ctx.CapesRepo.FindCapeByUsername(username)
|
||||
if (skinErr == nil && skin != nil && skin.SkinId != 0) || (capeErr == nil && cape != nil) {
|
||||
textures = &mojang.TexturesResponse{}
|
||||
if skinErr == nil && skin != nil && skin.SkinId != 0 {
|
||||
@ -185,7 +185,7 @@ func (ctx *Skinsystem) signedTexturesHandler(response http.ResponseWriter, reque
|
||||
|
||||
var responseData *mojang.SignedTexturesResponse
|
||||
|
||||
rec, err := ctx.SkinsRepo.FindByUsername(username)
|
||||
rec, err := ctx.SkinsRepo.FindSkinByUsername(username)
|
||||
if err == nil && rec != nil && rec.SkinId != 0 && rec.MojangTextures != "" {
|
||||
responseData = &mojang.SignedTexturesResponse{
|
||||
Id: strings.Replace(rec.Uuid, "-", "", -1),
|
||||
|
@ -26,7 +26,7 @@ type skinsRepositoryMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *skinsRepositoryMock) FindByUsername(username string) (*model.Skin, error) {
|
||||
func (m *skinsRepositoryMock) FindSkinByUsername(username string) (*model.Skin, error) {
|
||||
args := m.Called(username)
|
||||
var result *model.Skin
|
||||
if casted, ok := args.Get(0).(*model.Skin); ok {
|
||||
@ -36,7 +36,7 @@ func (m *skinsRepositoryMock) FindByUsername(username string) (*model.Skin, erro
|
||||
return result, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *skinsRepositoryMock) FindByUserId(id int) (*model.Skin, error) {
|
||||
func (m *skinsRepositoryMock) FindSkinByUserId(id int) (*model.Skin, error) {
|
||||
args := m.Called(id)
|
||||
var result *model.Skin
|
||||
if casted, ok := args.Get(0).(*model.Skin); ok {
|
||||
@ -46,17 +46,17 @@ func (m *skinsRepositoryMock) FindByUserId(id int) (*model.Skin, error) {
|
||||
return result, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *skinsRepositoryMock) Save(skin *model.Skin) error {
|
||||
func (m *skinsRepositoryMock) SaveSkin(skin *model.Skin) error {
|
||||
args := m.Called(skin)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *skinsRepositoryMock) RemoveByUserId(id int) error {
|
||||
func (m *skinsRepositoryMock) RemoveSkinByUserId(id int) error {
|
||||
args := m.Called(id)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *skinsRepositoryMock) RemoveByUsername(username string) error {
|
||||
func (m *skinsRepositoryMock) RemoveSkinByUsername(username string) error {
|
||||
args := m.Called(username)
|
||||
return args.Error(0)
|
||||
}
|
||||
@ -65,7 +65,7 @@ type capesRepositoryMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *capesRepositoryMock) FindByUsername(username string) (*model.Cape, error) {
|
||||
func (m *capesRepositoryMock) FindCapeByUsername(username string) (*model.Cape, error) {
|
||||
args := m.Called(username)
|
||||
var result *model.Cape
|
||||
if casted, ok := args.Get(0).(*model.Cape); ok {
|
||||
@ -155,7 +155,7 @@ var skinsTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username exists in the local storage",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(301, response.StatusCode)
|
||||
@ -165,7 +165,7 @@ var skinsTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username doesn't exists on the local storage, but exists on Mojang and has textures",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(true, false), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -176,7 +176,7 @@ var skinsTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username doesn't exists on the local storage, but exists on Mojang and has no textures",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(false, false), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -186,7 +186,7 @@ var skinsTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username doesn't exists on the local storage and doesn't exists on Mojang",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(nil, nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -210,7 +210,7 @@ func (suite *skinsystemTestSuite) TestSkin() {
|
||||
}
|
||||
|
||||
suite.RunSubTest("Pass username with png extension", func() {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
|
||||
req := httptest.NewRequest("GET", "http://chrly/skins/mock_username.png", nil)
|
||||
w := httptest.NewRecorder()
|
||||
@ -257,7 +257,7 @@ var capesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username exists in the local storage",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(createCapeModel(), nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(createCapeModel(), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(200, response.StatusCode)
|
||||
@ -269,7 +269,7 @@ var capesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username doesn't exists on the local storage, but exists on Mojang and has textures",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(true, true), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -280,7 +280,7 @@ var capesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username doesn't exists on the local storage, but exists on Mojang and has no textures",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(false, false), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -290,7 +290,7 @@ var capesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username doesn't exists on the local storage and doesn't exists on Mojang",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(nil, nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -314,7 +314,7 @@ func (suite *skinsystemTestSuite) TestCape() {
|
||||
}
|
||||
|
||||
suite.RunSubTest("Pass username with png extension", func() {
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(createCapeModel(), nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(createCapeModel(), nil)
|
||||
|
||||
req := httptest.NewRequest("GET", "http://chrly/cloaks/mock_username.png", nil)
|
||||
w := httptest.NewRecorder()
|
||||
@ -363,8 +363,8 @@ var texturesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username exists and has skin, no cape",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(200, response.StatusCode)
|
||||
@ -380,8 +380,8 @@ var texturesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username exists and has slim skin, no cape",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(createSkinModel("mock_username", true), nil)
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(createSkinModel("mock_username", true), nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(200, response.StatusCode)
|
||||
@ -400,8 +400,8 @@ var texturesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username exists and has cape, no skin",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(createCapeModel(), nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(createCapeModel(), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(200, response.StatusCode)
|
||||
@ -417,8 +417,8 @@ var texturesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username exists and has both skin and cape",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(createCapeModel(), nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(createSkinModel("mock_username", false), nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(createCapeModel(), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(200, response.StatusCode)
|
||||
@ -437,8 +437,8 @@ var texturesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username not exists, but Mojang profile available",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Once().Return(createMojangResponse(true, true), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -458,8 +458,8 @@ var texturesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username not exists, but Mojang profile available, but there is no textures",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Once().Return(createMojangResponse(false, false), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -469,8 +469,8 @@ var texturesTestsCases = []*skinsystemTestCase{
|
||||
{
|
||||
Name: "Username not exists and Mojang profile unavailable",
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.CapesRepository.On("FindCapeByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Once().Return(nil, nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -512,7 +512,7 @@ var signedTexturesTestsCases = []*signedTexturesTestCase{
|
||||
Name: "Username exists",
|
||||
AllowProxy: false,
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(createSkinModel("mock_username", true), nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(createSkinModel("mock_username", true), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(200, response.StatusCode)
|
||||
@ -539,7 +539,7 @@ var signedTexturesTestsCases = []*signedTexturesTestCase{
|
||||
Name: "Username not exists",
|
||||
AllowProxy: false,
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(204, response.StatusCode)
|
||||
@ -554,7 +554,7 @@ var signedTexturesTestsCases = []*signedTexturesTestCase{
|
||||
skinModel := createSkinModel("mock_username", true)
|
||||
skinModel.MojangTextures = ""
|
||||
skinModel.MojangSignature = ""
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(skinModel, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(skinModel, nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
suite.Equal(204, response.StatusCode)
|
||||
@ -566,7 +566,7 @@ var signedTexturesTestsCases = []*signedTexturesTestCase{
|
||||
Name: "Username not exists, but Mojang profile is available and proxying is enabled",
|
||||
AllowProxy: true,
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(createMojangResponse(true, false), nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
@ -593,7 +593,7 @@ var signedTexturesTestsCases = []*signedTexturesTestCase{
|
||||
Name: "Username not exists, Mojang profile is unavailable too and proxying is enabled",
|
||||
AllowProxy: true,
|
||||
BeforeTest: func(suite *skinsystemTestSuite) {
|
||||
suite.SkinsRepository.On("FindByUsername", "mock_username").Return(nil, nil)
|
||||
suite.SkinsRepository.On("FindSkinByUsername", "mock_username").Return(nil, nil)
|
||||
suite.MojangTexturesProvider.On("GetForUsername", "mock_username").Return(nil, nil)
|
||||
},
|
||||
AfterTest: func(suite *skinsystemTestSuite, response *http.Response) {
|
||||
|
Loading…
Reference in New Issue
Block a user