mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
test: add first few unit tests
This commit is contained in:
parent
35cdc7b485
commit
ad8168801c
@ -52,12 +52,17 @@ To use the hosted version set `api_url = https://wakapi.dev/api/heartbeat`. Howe
|
|||||||
**Note:** By default, the application is running in dev mode. However, it is recommended to set `ENV=production` for enhanced performance and security. To still be able to log in when using production mode, you either have to run Wakapi behind a reverse proxy, that enables for HTTPS encryption (see [best practices](#best-practices)) or set `security.insecure_cookies` to `true` in `config.yml`.
|
**Note:** By default, the application is running in dev mode. However, it is recommended to set `ENV=production` for enhanced performance and security. To still be able to log in when using production mode, you either have to run Wakapi behind a reverse proxy, that enables for HTTPS encryption (see [best practices](#best-practices)) or set `security.insecure_cookies` to `true` in `config.yml`.
|
||||||
|
|
||||||
### Run with Docker
|
### Run with Docker
|
||||||
```
|
```bash
|
||||||
docker run -d -p 3000:3000 --name wakapi n1try/wakapi
|
docker run -d -p 3000:3000 --name wakapi n1try/wakapi
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, SQLite is used as a database. To run Wakapi in Docker with MySQL or Postgres, see [Dockerfile](https://github.com/muety/wakapi/blob/master/Dockerfile) and [config.default.yml](https://github.com/muety/wakapi/blob/master/config.default.yml) for further options.
|
By default, SQLite is used as a database. To run Wakapi in Docker with MySQL or Postgres, see [Dockerfile](https://github.com/muety/wakapi/blob/master/Dockerfile) and [config.default.yml](https://github.com/muety/wakapi/blob/master/config.default.yml) for further options.
|
||||||
|
|
||||||
|
### Running tests
|
||||||
|
```bash
|
||||||
|
CGO_FLAGS="-g -O2 -Wno-return-local-addr" go test ./...
|
||||||
|
```
|
||||||
|
|
||||||
## 🔧 Configuration
|
## 🔧 Configuration
|
||||||
You can specify configuration options either via a config file (default: `config.yml`, customziable through the `-c` argument) or via environment variables. Here is an overview of all options.
|
You can specify configuration options either via a config file (default: `config.yml`, customziable through the `-c` argument) or via environment variables. Here is an overview of all options.
|
||||||
|
|
||||||
|
66
config/config_test.go
Normal file
66
config/config_test.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig_IsDev(t *testing.T) {
|
||||||
|
assert.True(t, IsDev("dev"))
|
||||||
|
assert.True(t, IsDev("development"))
|
||||||
|
assert.False(t, IsDev("prod"))
|
||||||
|
assert.False(t, IsDev("production"))
|
||||||
|
assert.False(t, IsDev("anything else"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_mysqlConnectionString(t *testing.T) {
|
||||||
|
c := &dbConfig{
|
||||||
|
Host: "test_host",
|
||||||
|
Port: 9999,
|
||||||
|
User: "test_user",
|
||||||
|
Password: "test_password",
|
||||||
|
Name: "test_name",
|
||||||
|
Dialect: "mysql",
|
||||||
|
MaxConn: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fmt.Sprintf(
|
||||||
|
"%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=true&loc=%s&sql_mode=ANSI_QUOTES",
|
||||||
|
c.User,
|
||||||
|
c.Password,
|
||||||
|
c.Host,
|
||||||
|
c.Port,
|
||||||
|
c.Name,
|
||||||
|
"Local",
|
||||||
|
), mysqlConnectionString(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_postgresConnectionString(t *testing.T) {
|
||||||
|
c := &dbConfig{
|
||||||
|
Host: "test_host",
|
||||||
|
Port: 9999,
|
||||||
|
User: "test_user",
|
||||||
|
Password: "test_password",
|
||||||
|
Name: "test_name",
|
||||||
|
Dialect: "postgres",
|
||||||
|
MaxConn: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fmt.Sprintf(
|
||||||
|
"host=%s port=%d user=%s dbname=%s password=%s sslmode=disable",
|
||||||
|
c.Host,
|
||||||
|
c.Port,
|
||||||
|
c.User,
|
||||||
|
c.Name,
|
||||||
|
c.Password,
|
||||||
|
), postgresConnectionString(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_sqliteConnectionString(t *testing.T) {
|
||||||
|
c := &dbConfig{
|
||||||
|
Name: "test_name",
|
||||||
|
Dialect: "sqlite3",
|
||||||
|
}
|
||||||
|
assert.Equal(t, c.Name, sqliteConnectionString(c))
|
||||||
|
}
|
1
go.mod
1
go.mod
@ -16,6 +16,7 @@ require (
|
|||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc
|
github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
|
github.com/stretchr/testify v1.6.1
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
gopkg.in/ini.v1 v1.50.0
|
gopkg.in/ini.v1 v1.50.0
|
||||||
|
5
go.sum
5
go.sum
@ -382,12 +382,15 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3
|
|||||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
@ -554,6 +557,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg=
|
gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg=
|
||||||
gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI=
|
gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI=
|
||||||
gorm.io/driver/postgres v1.0.5 h1:raX6ezL/ciUmaYTvOq48jq1GE95aMC0CmxQYbxQ4Ufw=
|
gorm.io/driver/postgres v1.0.5 h1:raX6ezL/ciUmaYTvOq48jq1GE95aMC0CmxQYbxQ4Ufw=
|
||||||
|
2
main.go
2
main.go
@ -102,6 +102,8 @@ func main() {
|
|||||||
|
|
||||||
// TODO: move endpoint registration to the respective routes files
|
// TODO: move endpoint registration to the respective routes files
|
||||||
|
|
||||||
|
routes.Init()
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
summaryHandler := routes.NewSummaryHandler(summaryService)
|
summaryHandler := routes.NewSummaryHandler(summaryService)
|
||||||
healthHandler := routes.NewHealthHandler(db)
|
healthHandler := routes.NewHealthHandler(db)
|
||||||
|
81
middlewares/authenticate_test.go
Normal file
81
middlewares/authenticate_test.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/muety/wakapi/mocks"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAuthenticateMiddleware_tryGetUserByApiKey_Success(t *testing.T) {
|
||||||
|
testApiKey := "z5uig69cn9ut93n"
|
||||||
|
testToken := base64.StdEncoding.EncodeToString([]byte(testApiKey))
|
||||||
|
testUser := &models.User{ApiKey: testApiKey}
|
||||||
|
|
||||||
|
mockRequest := &http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"Authorization": []string{fmt.Sprintf("Basic %s", testToken)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
userServiceMock := new(mocks.UserServiceMock)
|
||||||
|
userServiceMock.On("GetUserByKey", testApiKey).Return(testUser, nil)
|
||||||
|
|
||||||
|
sut := NewAuthenticateMiddleware(userServiceMock, []string{})
|
||||||
|
|
||||||
|
result, err := sut.tryGetUserByApiKey(mockRequest)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, testUser, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthenticateMiddleware_tryGetUserByApiKey_GetFromCache(t *testing.T) {
|
||||||
|
testApiKey := "z5uig69cn9ut93n"
|
||||||
|
testToken := base64.StdEncoding.EncodeToString([]byte(testApiKey))
|
||||||
|
testUser := &models.User{ApiKey: testApiKey}
|
||||||
|
|
||||||
|
mockRequest := &http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"Authorization": []string{fmt.Sprintf("Basic %s", testToken)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
userServiceMock := new(mocks.UserServiceMock)
|
||||||
|
userServiceMock.On("GetUserByKey", testApiKey).Return(testUser, nil)
|
||||||
|
|
||||||
|
sut := NewAuthenticateMiddleware(userServiceMock, []string{})
|
||||||
|
sut.cache.SetDefault(testApiKey, testUser)
|
||||||
|
|
||||||
|
result, err := sut.tryGetUserByApiKey(mockRequest)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, testUser, result)
|
||||||
|
userServiceMock.AssertNotCalled(t, "GetUserByKey", mock.Anything)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthenticateMiddleware_tryGetUserByApiKey_InvalidHeader(t *testing.T) {
|
||||||
|
testApiKey := "z5uig69cn9ut93n"
|
||||||
|
testToken := base64.StdEncoding.EncodeToString([]byte(testApiKey))
|
||||||
|
|
||||||
|
mockRequest := &http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
// 'Basic' prefix missing here
|
||||||
|
"Authorization": []string{fmt.Sprintf("%s", testToken)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
userServiceMock := new(mocks.UserServiceMock)
|
||||||
|
|
||||||
|
sut := NewAuthenticateMiddleware(userServiceMock, []string{})
|
||||||
|
|
||||||
|
result, err := sut.tryGetUserByApiKey(mockRequest)
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: somehow test cookie auth function
|
@ -22,7 +22,7 @@ func init() {
|
|||||||
|
|
||||||
func RunCustomPostMigrations(db *gorm.DB, cfg *config.Config) {
|
func RunCustomPostMigrations(db *gorm.DB, cfg *config.Config) {
|
||||||
for _, m := range customPostMigrations {
|
for _, m := range customPostMigrations {
|
||||||
log.Printf("running migration '%s'\n", m.name)
|
log.Printf("potentially running migration '%s'\n", m.name)
|
||||||
if err := m.f(db, cfg); err != nil {
|
if err := m.f(db, cfg); err != nil {
|
||||||
log.Fatalf("migration '%s' failed – %v\n", m.name, err)
|
log.Fatalf("migration '%s' failed – %v\n", m.name, err)
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ func init() {
|
|||||||
|
|
||||||
func RunCustomPreMigrations(db *gorm.DB, cfg *config.Config) {
|
func RunCustomPreMigrations(db *gorm.DB, cfg *config.Config) {
|
||||||
for _, m := range customPreMigrations {
|
for _, m := range customPreMigrations {
|
||||||
log.Printf("running migration '%s'\n", m.name)
|
log.Printf("potentially running migration '%s'\n", m.name)
|
||||||
if err := m.f(db, cfg); err != nil {
|
if err := m.f(db, cfg); err != nil {
|
||||||
log.Fatalf("migration '%s' failed – %v\n", m.name, err)
|
log.Fatalf("migration '%s' failed – %v\n", m.name, err)
|
||||||
}
|
}
|
||||||
|
15
mocks/alias_repository_mock.go
Normal file
15
mocks/alias_repository_mock.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliasRepositoryMock struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AliasRepositoryMock) GetByUser(s string) ([]*models.Alias, error) {
|
||||||
|
args := m.Called(s)
|
||||||
|
return args.Get(0).([]*models.Alias), args.Error(1)
|
||||||
|
}
|
50
mocks/user_service_mock.go
Normal file
50
mocks/user_service_mock.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserServiceMock struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) GetUserById(s string) (*models.User, error) {
|
||||||
|
args := m.Called(s)
|
||||||
|
return args.Get(0).(*models.User), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) GetUserByKey(s string) (*models.User, error) {
|
||||||
|
args := m.Called(s)
|
||||||
|
return args.Get(0).(*models.User), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) GetAll() ([]*models.User, error) {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Get(0).([]*models.User), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) CreateOrGet(signup *models.Signup) (*models.User, bool, error) {
|
||||||
|
args := m.Called(signup)
|
||||||
|
return args.Get(0).(*models.User), args.Bool(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) Update(user *models.User) (*models.User, error) {
|
||||||
|
args := m.Called(user)
|
||||||
|
return args.Get(0).(*models.User), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) ResetApiKey(user *models.User) (*models.User, error) {
|
||||||
|
args := m.Called(user)
|
||||||
|
return args.Get(0).(*models.User), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) ToggleBadges(user *models.User) (*models.User, error) {
|
||||||
|
args := m.Called(user)
|
||||||
|
return args.Get(0).(*models.User), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) MigrateMd5Password(user *models.User, login *models.Login) (*models.User, error) {
|
||||||
|
args := m.Called(user, login)
|
||||||
|
return args.Get(0).(*models.User), args.Error(1)
|
||||||
|
}
|
@ -24,7 +24,7 @@ type Heartbeat struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Heartbeat) Valid() bool {
|
func (h *Heartbeat) Valid() bool {
|
||||||
return h.User != nil && h.UserID != "" && h.Time != CustomTime(time.Time{})
|
return h.User != nil && h.UserID != "" && h.User.ID == h.UserID && h.Time != CustomTime(time.Time{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Heartbeat) Augment(languageMappings map[string]string) {
|
func (h *Heartbeat) Augment(languageMappings map[string]string) {
|
||||||
|
53
models/heartbeat_test.go
Normal file
53
models/heartbeat_test.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHeartbeat_Valid_Success(t *testing.T) {
|
||||||
|
sut := &Heartbeat{
|
||||||
|
User: &User{
|
||||||
|
ID: "johndoe@example.org",
|
||||||
|
},
|
||||||
|
UserID: "johndoe@example.org",
|
||||||
|
Time: CustomTime(time.Now()),
|
||||||
|
}
|
||||||
|
assert.True(t, sut.Valid())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeartbeat_Valid_MissingUser(t *testing.T) {
|
||||||
|
sut := &Heartbeat{
|
||||||
|
Time: CustomTime(time.Now()),
|
||||||
|
}
|
||||||
|
assert.False(t, sut.Valid())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeartbeat_Augment(t *testing.T) {
|
||||||
|
testMappings := map[string]string{
|
||||||
|
"py": "Python3",
|
||||||
|
}
|
||||||
|
|
||||||
|
sut := &Heartbeat{
|
||||||
|
Entity: "~/dev/file.py",
|
||||||
|
Language: "Python",
|
||||||
|
}
|
||||||
|
|
||||||
|
sut.Augment(testMappings)
|
||||||
|
|
||||||
|
assert.Equal(t, "Python3", sut.Language)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeartbeat_GetKey(t *testing.T) {
|
||||||
|
sut := &Heartbeat{
|
||||||
|
Project: "wakapi",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "wakapi", sut.GetKey(SummaryProject))
|
||||||
|
assert.Equal(t, UnknownSummaryKey, sut.GetKey(SummaryOS))
|
||||||
|
assert.Equal(t, UnknownSummaryKey, sut.GetKey(SummaryMachine))
|
||||||
|
assert.Equal(t, UnknownSummaryKey, sut.GetKey(SummaryLanguage))
|
||||||
|
assert.Equal(t, UnknownSummaryKey, sut.GetKey(SummaryEditor))
|
||||||
|
assert.Equal(t, UnknownSummaryKey, sut.GetKey(255))
|
||||||
|
}
|
165
models/summary_test.go
Normal file
165
models/summary_test.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSummary_FillUnknown(t *testing.T) {
|
||||||
|
testDuration := 10 * time.Minute
|
||||||
|
|
||||||
|
sut := &Summary{
|
||||||
|
Projects: []*SummaryItem{
|
||||||
|
{
|
||||||
|
Type: SummaryProject,
|
||||||
|
Key: "wakapi",
|
||||||
|
// hack to work around the issue that the total time of a summary item is mistakenly represented in seconds
|
||||||
|
Total: testDuration / time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sut.FillUnknown()
|
||||||
|
|
||||||
|
itemLists := [][]*SummaryItem{
|
||||||
|
sut.Machines,
|
||||||
|
sut.OperatingSystems,
|
||||||
|
sut.Languages,
|
||||||
|
sut.Editors,
|
||||||
|
}
|
||||||
|
for _, l := range itemLists {
|
||||||
|
assert.Len(t, l, 1)
|
||||||
|
assert.Equal(t, UnknownSummaryKey, l[0].Key)
|
||||||
|
assert.Equal(t, testDuration, l[0].Total)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummary_TotalTimeBy(t *testing.T) {
|
||||||
|
testDuration1, testDuration2, testDuration3 := 10*time.Minute, 5*time.Minute, 20*time.Minute
|
||||||
|
|
||||||
|
sut := &Summary{
|
||||||
|
Projects: []*SummaryItem{
|
||||||
|
{
|
||||||
|
Type: SummaryProject,
|
||||||
|
Key: "wakapi",
|
||||||
|
// hack to work around the issue that the total time of a summary item is mistakenly represented in seconds
|
||||||
|
Total: testDuration1 / time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: SummaryProject,
|
||||||
|
Key: "anchr",
|
||||||
|
Total: testDuration2 / time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Languages: []*SummaryItem{
|
||||||
|
{
|
||||||
|
Type: SummaryLanguage,
|
||||||
|
Key: "Go",
|
||||||
|
Total: testDuration3 / time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testDuration1+testDuration2, sut.TotalTimeBy(SummaryProject))
|
||||||
|
assert.Equal(t, testDuration3, sut.TotalTimeBy(SummaryLanguage))
|
||||||
|
assert.Zero(t, sut.TotalTimeBy(SummaryEditor))
|
||||||
|
assert.Zero(t, sut.TotalTimeBy(SummaryMachine))
|
||||||
|
assert.Zero(t, sut.TotalTimeBy(SummaryOS))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummary_TotalTimeByFilters(t *testing.T) {
|
||||||
|
testDuration1, testDuration2, testDuration3 := 10*time.Minute, 5*time.Minute, 20*time.Minute
|
||||||
|
|
||||||
|
sut := &Summary{
|
||||||
|
Projects: []*SummaryItem{
|
||||||
|
{
|
||||||
|
Type: SummaryProject,
|
||||||
|
Key: "wakapi",
|
||||||
|
// hack to work around the issue that the total time of a summary item is mistakenly represented in seconds
|
||||||
|
Total: testDuration1 / time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: SummaryProject,
|
||||||
|
Key: "anchr",
|
||||||
|
Total: testDuration2 / time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Languages: []*SummaryItem{
|
||||||
|
{
|
||||||
|
Type: SummaryLanguage,
|
||||||
|
Key: "Go",
|
||||||
|
Total: testDuration3 / time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
filters1 := &Filters{Project: "wakapi"}
|
||||||
|
filters2 := &Filters{Project: "wakapi", Language: "Go"} // filters have OR logic
|
||||||
|
filters3 := &Filters{}
|
||||||
|
|
||||||
|
assert.Equal(t, testDuration1, sut.TotalTimeByFilters(filters1))
|
||||||
|
assert.Equal(t, testDuration1+testDuration3, sut.TotalTimeByFilters(filters2))
|
||||||
|
assert.Zero(t, sut.TotalTimeByFilters(filters3))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummary_WithResolvedAliases(t *testing.T) {
|
||||||
|
testDuration1, testDuration2, testDuration3, testDuration4 := 10*time.Minute, 5*time.Minute, 1*time.Minute, 20*time.Minute
|
||||||
|
|
||||||
|
var resolver AliasResolver = func(t uint8, k string) string {
|
||||||
|
switch t {
|
||||||
|
case SummaryProject:
|
||||||
|
switch k {
|
||||||
|
case "wakapi-mobile":
|
||||||
|
return "wakapi"
|
||||||
|
}
|
||||||
|
case SummaryLanguage:
|
||||||
|
switch k {
|
||||||
|
case "Java 8":
|
||||||
|
return "Java"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
sut := &Summary{
|
||||||
|
Projects: []*SummaryItem{
|
||||||
|
{
|
||||||
|
Type: SummaryProject,
|
||||||
|
Key: "wakapi",
|
||||||
|
Total: testDuration1 / time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: SummaryProject,
|
||||||
|
Key: "wakapi-mobile",
|
||||||
|
Total: testDuration2 / time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: SummaryProject,
|
||||||
|
Key: "anchr",
|
||||||
|
Total: testDuration3 / time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Languages: []*SummaryItem{
|
||||||
|
{
|
||||||
|
Type: SummaryLanguage,
|
||||||
|
Key: "Java 8",
|
||||||
|
Total: testDuration4 / time.Second,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sut = sut.WithResolvedAliases(resolver)
|
||||||
|
|
||||||
|
assert.Equal(t, testDuration1+testDuration2, sut.TotalTimeByKey(SummaryProject, "wakapi"))
|
||||||
|
assert.Zero(t, sut.TotalTimeByKey(SummaryProject, "wakapi-mobile"))
|
||||||
|
assert.Equal(t, testDuration3, sut.TotalTimeByKey(SummaryProject, "anchr"))
|
||||||
|
assert.Equal(t, testDuration4, sut.TotalTimeByKey(SummaryLanguage, "Java"))
|
||||||
|
assert.Zero(t, sut.TotalTimeByKey(SummaryLanguage, "wakapi"))
|
||||||
|
assert.Zero(t, sut.TotalTimeByKey(SummaryProject, "Java 8"))
|
||||||
|
assert.Len(t, sut.Projects, 2)
|
||||||
|
assert.Len(t, sut.Languages, 1)
|
||||||
|
assert.Empty(t, sut.Editors)
|
||||||
|
assert.Empty(t, sut.OperatingSystems)
|
||||||
|
assert.Empty(t, sut.Machines)
|
||||||
|
}
|
@ -10,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func Init() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
63
services/alias_test.go
Normal file
63
services/alias_test.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/muety/wakapi/mocks"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliasServiceTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
TestUserId string
|
||||||
|
AliasRepoMock *mocks.AliasRepositoryMock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AliasServiceTestSuite) SetupSuite() {
|
||||||
|
suite.TestUserId = "johndoe@example.org"
|
||||||
|
|
||||||
|
aliases := []*models.Alias{
|
||||||
|
{
|
||||||
|
Type: models.SummaryProject,
|
||||||
|
UserID: suite.TestUserId,
|
||||||
|
Key: "wakapi",
|
||||||
|
Value: "wakapi-mobile",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasRepoMock := new(mocks.AliasRepositoryMock)
|
||||||
|
aliasRepoMock.On("GetByUser", suite.TestUserId).Return(aliases, nil)
|
||||||
|
aliasRepoMock.On("GetByUser", mock.AnythingOfType("string")).Return([]*models.Alias{}, assert.AnError)
|
||||||
|
|
||||||
|
suite.AliasRepoMock = aliasRepoMock
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAliasServiceTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(AliasServiceTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AliasServiceTestSuite) TestAliasService_GetAliasOrDefault() {
|
||||||
|
sut := NewAliasService(suite.AliasRepoMock)
|
||||||
|
|
||||||
|
result1, err1 := sut.GetAliasOrDefault(suite.TestUserId, models.SummaryProject, "wakapi-mobile")
|
||||||
|
result2, err2 := sut.GetAliasOrDefault(suite.TestUserId, models.SummaryProject, "wakapi")
|
||||||
|
result3, err3 := sut.GetAliasOrDefault(suite.TestUserId, models.SummaryProject, "anchr")
|
||||||
|
|
||||||
|
assert.Equal(suite.T(), "wakapi", result1)
|
||||||
|
assert.Nil(suite.T(), err1)
|
||||||
|
assert.Equal(suite.T(), "wakapi", result2)
|
||||||
|
assert.Nil(suite.T(), err2)
|
||||||
|
assert.Equal(suite.T(), "anchr", result3)
|
||||||
|
assert.Nil(suite.T(), err3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AliasServiceTestSuite) TestAliasService_GetAliasOrDefault_ErrorOnNonExistingUser() {
|
||||||
|
sut := NewAliasService(suite.AliasRepoMock)
|
||||||
|
|
||||||
|
result, err := sut.GetAliasOrDefault("nonexisting", models.SummaryProject, "wakapi-mobile")
|
||||||
|
|
||||||
|
assert.Empty(suite.T(), result)
|
||||||
|
assert.Error(suite.T(), err)
|
||||||
|
}
|
@ -2,10 +2,11 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseUserAgent(t *testing.T) {
|
func TestCommon_ParseUserAgent(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in string
|
in string
|
||||||
outOs string
|
outOs string
|
||||||
@ -38,10 +39,11 @@ func TestParseUserAgent(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for _, test := range tests {
|
||||||
if os, editor, err := ParseUserAgent(test.in); os != test.outOs || editor != test.outEditor || !checkErr(test.outError, err) {
|
os, editor, err := ParseUserAgent(test.in)
|
||||||
t.Errorf("[%d] Unexpected result of parsing '%s'; got '%v', '%v', '%v'", i, test.in, os, editor, err)
|
assert.True(t, checkErr(err, test.outError))
|
||||||
}
|
assert.Equal(t, test.outOs, os)
|
||||||
|
assert.Equal(t, test.outEditor, editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
1.16.2
|
1.16.3
|
||||||
|
Loading…
Reference in New Issue
Block a user