Browse Source

Refactor User and DB handling (#123)

fix copyright date

Add more user tests

More user tests

More user tests

Start refactoring user tests

Docs

Fix lint

Fix db fixtures init in tests

Fix models test

Fix loading fixtures

Fix ineffasign

Fix lint

Fix integration tests

Fix init of test engine creation

Fix user related tests

Better handling of creating test enging

Moved all fixtures to db package

Moved all fixtures to db package

Moved user related stuff to seperate package

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/api#123
release/0.13
konrad 2 years ago
parent
commit
7e9446ea07
  1. 37
      docs/content/doc/development/structure.md
  2. 26
      docs/content/doc/development/test.md
  3. 1
      go.mod
  4. 4
      pkg/caldav/caldav.go
  5. 5
      pkg/cmd/cmd.go
  6. 28
      pkg/db/db.go
  7. 0
      pkg/db/fixtures/files.yml
  8. 0
      pkg/db/fixtures/label_task.yml
  9. 0
      pkg/db/fixtures/labels.yml
  10. 0
      pkg/db/fixtures/link_sharing.yml
  11. 0
      pkg/db/fixtures/list.yml
  12. 0
      pkg/db/fixtures/namespaces.yml
  13. 0
      pkg/db/fixtures/task_assignees.yml
  14. 0
      pkg/db/fixtures/task_attachments.yml
  15. 0
      pkg/db/fixtures/task_relations.yml
  16. 0
      pkg/db/fixtures/task_reminders.yml
  17. 0
      pkg/db/fixtures/tasks.yml
  18. 0
      pkg/db/fixtures/team_list.yml
  19. 0
      pkg/db/fixtures/team_members.yml
  20. 0
      pkg/db/fixtures/team_namespaces.yml
  21. 0
      pkg/db/fixtures/teams.yml
  22. 0
      pkg/db/fixtures/users.yml
  23. 0
      pkg/db/fixtures/users_list.yml
  24. 0
      pkg/db/fixtures/users_namespace.yml
  25. 69
      pkg/db/test.go
  26. 31
      pkg/db/test_fixtures.go
  27. 21
      pkg/files/filehandling.go
  28. 20
      pkg/integrations/integrations.go
  29. 10
      pkg/integrations/login_test.go
  30. 14
      pkg/integrations/register_test.go
  31. 8
      pkg/integrations/user_change_password_test.go
  32. 8
      pkg/integrations/user_confirm_email_test.go
  33. 6
      pkg/integrations/user_password_request_token_test.go
  34. 6
      pkg/integrations/user_password_reset_test.go
  35. 4
      pkg/migration/migration.go
  36. 12
      pkg/models/bulk_task_test.go
  37. 267
      pkg/models/error.go
  38. 1
      pkg/models/fixtures/files.yml
  39. 11
      pkg/models/label.go
  40. 3
      pkg/models/label_rights.go
  41. 9
      pkg/models/label_task.go
  42. 38
      pkg/models/label_task_test.go
  43. 49
      pkg/models/label_test.go
  44. 9
      pkg/models/link_sharing.go
  45. 17
      pkg/models/list.go
  46. 9
      pkg/models/list_rights.go
  47. 8
      pkg/models/list_team_test.go
  48. 50
      pkg/models/list_test.go
  49. 15
      pkg/models/list_users.go
  50. 10
      pkg/models/list_users_rights_test.go
  51. 24
      pkg/models/list_users_test.go
  52. 5
      pkg/models/main_test.go
  53. 3
      pkg/models/models.go
  54. 17
      pkg/models/namespace.go
  55. 10
      pkg/models/namespace_team_rights_test.go
  56. 10
      pkg/models/namespace_team_test.go
  57. 12
      pkg/models/namespace_test.go
  58. 11
      pkg/models/namespace_users.go
  59. 10
      pkg/models/namespace_users_rights_test.go
  60. 24
      pkg/models/namespace_users_test.go
  61. 25
      pkg/models/task_assignees.go
  62. 7
      pkg/models/task_attachment.go
  63. 7
      pkg/models/task_attachment_test.go
  64. 3
      pkg/models/task_collection.go
  65. 24
      pkg/models/task_collection_test.go
  66. 3
      pkg/models/task_relation.go
  67. 50
      pkg/models/task_relation_test.go
  68. 9
      pkg/models/tasks.go
  69. 34
      pkg/models/tasks_test.go
  70. 9
      pkg/models/team_members.go
  71. 7
      pkg/models/team_members_test.go
  72. 9
      pkg/models/teams.go
  73. 12
      pkg/models/teams_rights_test.go
  74. 3
      pkg/models/teams_test.go
  75. 89
      pkg/models/unit_tests.go
  76. 36
      pkg/models/user_list.go
  77. 178
      pkg/models/user_test.go
  78. 57
      pkg/models/users_list_test.go
  79. 3
      pkg/modules/migration/create_from_structure.go
  80. 5
      pkg/modules/migration/handler/handler.go
  81. 8
      pkg/modules/migration/migration_status.go
  82. 6
      pkg/modules/migration/migrator.go
  83. 3
      pkg/modules/migration/wunderlist/wunderlist.go
  84. 5
      pkg/routes/api/v1/auth.go
  85. 5
      pkg/routes/api/v1/list_by_namespace.go
  86. 7
      pkg/routes/api/v1/login.go
  87. 5
      pkg/routes/api/v1/task_attachment.go
  88. 12
      pkg/routes/api/v1/user_add_update.go
  89. 5
      pkg/routes/api/v1/user_confirm_email.go
  90. 65
      pkg/routes/api/v1/user_delete.go
  91. 5
      pkg/routes/api/v1/user_list.go
  92. 9
      pkg/routes/api/v1/user_password_reset.go
  93. 6
      pkg/routes/api/v1/user_show.go
  94. 9
      pkg/routes/api/v1/user_update_password.go
  95. 7
      pkg/routes/caldav/handler.go
  96. 3
      pkg/routes/caldav/listStorageProvider.go
  97. 3
      pkg/routes/metrics.go
  98. 5
      pkg/routes/routes.go
  99. 50
      pkg/user/db.go
  100. 291
      pkg/user/error.go

37
docs/content/doc/development/structure.md

@ -16,7 +16,12 @@ In general, this api repo has the following structure:
* `docs`
* `pkg`
* `caldav`
* `cmd`
* `config`
* `db`
* `fixtures`
* `files`
* `integration`
* `log`
* `mail`
* `metrics`
@ -29,8 +34,11 @@ In general, this api repo has the following structure:
* `red`
* `routes`
* `api/v1`
* `static`
* `swagger`
* `user`
* `utils`
* `version`
* `REST-Tests`
* `templates`
* `vendor`
@ -70,6 +78,21 @@ how to interpret which env variables for config etc.
If you want to add a new config parameter, you should add default value in this package.
### db
This package contains the db connection handling and db fixtures for testing.
Each other package gets its db connection object from this package.
### files
This package is responsible for all file-related things.
This means it handles saving and retrieving files from the db and the underlying file system.
### integration
All integration tests live here.
See [integration tests]({{< ref "test.md" >}}#integration-tests) for more details.
### log
Similar to `config`, this will set up the logging, based on differen logging backends.
@ -127,11 +150,19 @@ To add a new route, see [adding a new route]({{< ref "../practical-instructions/
This is where all http-handler functions for the api are stored.
Every handler function which does not use the standard web handler should live here.
### static
All static files generated by `make generate` live here.
### swagger
This is where the [generated]({{< ref "make.md#generate-swagger-definitions-from-code-comments">}} [api docs]({{< ref "../usage/api.md">}}) live.
You usually don't need to touch this package.
### user
All user-related things like registration etc. live in this package.
### utils
A small package, containing some helper functions:
@ -141,6 +172,12 @@ A small package, containing some helper functions:
See their function definitions for instructions on how to use them.
### version
The single purpouse of this package is to hold the current vikunja version which gets overridden through build flags
each time `make release` or `make build` is run.
It is a seperate package to avoid import cycles with other packages.
## REST-Tests
Holds all kinds of test files to directly test the api from inside of [jetbrains ide's](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html).

26
docs/content/doc/development/test.md

@ -42,3 +42,29 @@ The integration tests use the same config and fixtures as the unit tests and the
see at the beginning of this document.
To run integration tests, use `make integration-test`.
# Initializing db fixtures when writing tests
All db fixtures for all tests live in the `pkg/db/fixtures/` folder as yaml files.
Each file has the same name as the table the fixtures are for.
You should put new fixtures in this folder.
When initializing db fixtures, you are responsible for defining which tables your package needs in your test init function.
Usually, this is done as follows (this code snippet is taken from the `user` package):
```go
err = db.InitTestFixtures("users")
if err != nil {
log.Fatal(err)
}
```
In your actual tests, you then load the fixtures into the in-memory db like so:
```go
db.LoadAndAssertFixtures(t)
```
This will load all fixtures you defined in your test init method.
You should always use this method to load fixtures, the only exception is when your package tests require extra test
fixtures other than db fixtures (like files).

1
go.mod

@ -59,7 +59,6 @@ require (
github.com/onsi/gomega v1.4.3 // indirect
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml v1.4.0 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/client_golang v0.9.2
github.com/samedi/caldav-go v3.0.0+incompatible
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b

4
pkg/caldav/caldav.go

@ -17,7 +17,7 @@
package caldav
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
"fmt"
"strconv"
@ -49,7 +49,7 @@ type Todo struct {
Summary string
Description string
CompletedUnix int64
Organizer *models.User
Organizer *user.User
Priority int64 // 0-9, 1 is highest
RelatedToUID string

5
pkg/cmd/cmd.go

@ -25,6 +25,7 @@ import (
"code.vikunja.io/api/pkg/models"
migrator "code.vikunja.io/api/pkg/modules/migration"
"code.vikunja.io/api/pkg/red"
"code.vikunja.io/api/pkg/user"
"fmt"
"github.com/spf13/cobra"
"os"
@ -76,6 +77,10 @@ func initialize() {
if err != nil {
log.Fatal(err.Error())
}
err = user.InitDB()
if err != nil {
log.Fatal(err.Error())
}
err = files.SetEngine()
if err != nil {
log.Fatal(err.Error())

28
pkg/db/db.go

@ -23,7 +23,6 @@ import (
"fmt"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
"os"
"strconv"
"time"
@ -85,33 +84,6 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
return
}
// CreateTestEngine creates an instance of the db engine which lives in memory
func CreateTestEngine() (engine *xorm.Engine, err error) {
if x != nil {
return x, nil
}
if os.Getenv("VIKUNJA_TESTS_USE_CONFIG") == "1" {
config.InitConfig()
engine, err = CreateDBEngine()
if err != nil {
return nil, err
}
} else {
engine, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
if err != nil {
return nil, err
}
}
engine.SetMapper(core.GonicMapper{})
engine.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
engine.SetLogger(xorm.NewSimpleLogger(log.GetLogWriter("database")))
x = engine
return
}
// RegisterTableStructsForCache registers tables in gob encoding for redis cache
func RegisterTableStructsForCache(val interface{}) {
gob.Register(val)

0
pkg/files/fixtures/files.yml → pkg/db/fixtures/files.yml

0
pkg/models/fixtures/label_task.yml → pkg/db/fixtures/label_task.yml

0
pkg/models/fixtures/labels.yml → pkg/db/fixtures/labels.yml

0
pkg/models/fixtures/link_sharing.yml → pkg/db/fixtures/link_sharing.yml

0
pkg/models/fixtures/list.yml → pkg/db/fixtures/list.yml

0
pkg/models/fixtures/namespaces.yml → pkg/db/fixtures/namespaces.yml

0
pkg/models/fixtures/task_assignees.yml → pkg/db/fixtures/task_assignees.yml

0
pkg/models/fixtures/task_attachments.yml → pkg/db/fixtures/task_attachments.yml

0
pkg/models/fixtures/task_relations.yml → pkg/db/fixtures/task_relations.yml

0
pkg/models/fixtures/task_reminders.yml → pkg/db/fixtures/task_reminders.yml

0
pkg/models/fixtures/tasks.yml → pkg/db/fixtures/tasks.yml

0
pkg/models/fixtures/team_list.yml → pkg/db/fixtures/team_list.yml

0
pkg/models/fixtures/team_members.yml → pkg/db/fixtures/team_members.yml

0
pkg/models/fixtures/team_namespaces.yml → pkg/db/fixtures/team_namespaces.yml

0
pkg/models/fixtures/teams.yml → pkg/db/fixtures/teams.yml

0
pkg/models/fixtures/users.yml → pkg/db/fixtures/users.yml

0
pkg/models/fixtures/users_list.yml → pkg/db/fixtures/users_list.yml

0
pkg/models/fixtures/users_namespace.yml → pkg/db/fixtures/users_namespace.yml

69
pkg/db/test.go

@ -0,0 +1,69 @@
// Copyright2018-2020 Vikunja and contriubtors. All rights reserved.
//
// This file is part of Vikunja.
//
// Vikunja is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vikunja is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
package db
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
"os"
)
// CreateTestEngine creates an instance of the db engine which lives in memory
func CreateTestEngine() (engine *xorm.Engine, err error) {
if x != nil {
return x, nil
}
if os.Getenv("VIKUNJA_TESTS_USE_CONFIG") == "1" {
config.InitConfig()
engine, err = CreateDBEngine()
if err != nil {
return nil, err
}
} else {
engine, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
if err != nil {
return nil, err
}
}
engine.SetMapper(core.GonicMapper{})
engine.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
engine.SetLogger(xorm.NewSimpleLogger(log.GetLogWriter("database")))
x = engine
return
}
// InitTestFixtures populates the db with all fixtures from the fixtures folder
func InitTestFixtures(tablenames ...string) (err error) {
// Create all fixtures
config.InitDefaultConfig()
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
// Sync fixtures
err = InitFixtures(tablenames...)
if err != nil {
log.Fatal(err)
}
return nil
}

31
pkg/db/test_fixtures.go

@ -18,15 +18,36 @@
package db
import (
"code.vikunja.io/api/pkg/config"
"github.com/stretchr/testify/assert"
"gopkg.in/testfixtures.v2"
"path/filepath"
"testing"
)
var fixtures *testfixtures.Context
// InitFixtures initialize test fixtures for a test database
func InitFixtures(helper testfixtures.Helper, dir string) (err error) {
func InitFixtures(tablenames ...string) (err error) {
var helper testfixtures.Helper = &testfixtures.SQLite{}
if config.DatabaseType.GetString() == "mysql" {
helper = &testfixtures.MySQL{}
}
dir := filepath.Join(config.ServiceRootpath.GetString(), "pkg", "db", "fixtures")
testfixtures.SkipDatabaseNameCheck(true)
fixtures, err = testfixtures.NewFolder(x.DB().DB, helper, dir)
// If fixture table names are specified, load them
// Otherwise, load all fixtures
if len(tablenames) > 0 {
for i, name := range tablenames {
tablenames[i] = filepath.Join(dir, name+".yml")
}
fixtures, err = testfixtures.NewFiles(x.DB().DB, helper, tablenames...)
} else {
fixtures, err = testfixtures.NewFolder(x.DB().DB, helper, dir)
}
return err
}
@ -34,3 +55,9 @@ func InitFixtures(helper testfixtures.Helper, dir string) (err error) {
func LoadFixtures() error {
return fixtures.Load()
}
// LoadAndAssertFixtures loads all fixtures defined before and asserts they are correctly loaded
func LoadAndAssertFixtures(t *testing.T) {
err := LoadFixtures()
assert.NoError(t, err)
}

21
pkg/files/filehandling.go

@ -22,9 +22,7 @@ import (
"code.vikunja.io/api/pkg/log"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"gopkg.in/testfixtures.v2"
"os"
"path/filepath"
"testing"
)
@ -45,10 +43,9 @@ func InitTestFileHandler() {
}
func initFixtures(t *testing.T) {
// Init db fixtures
err := db.LoadFixtures()
assert.NoError(t, err)
// DB fixtures
db.LoadAndAssertFixtures(t)
// File fixtures
InitTestFileFixtures(t)
}
@ -73,17 +70,7 @@ func InitTests() {
log.Fatal(err)
}
config.InitDefaultConfig()
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
// Sync fixtures
var fixturesHelper testfixtures.Helper = &testfixtures.SQLite{}
if config.DatabaseType.GetString() == "mysql" {
fixturesHelper = &testfixtures.MySQL{}
}
fixturesDir := filepath.Join(config.ServiceRootpath.GetString(), "pkg", "files", "fixtures")
err = db.InitFixtures(fixturesHelper, fixturesDir)
err = db.InitTestFixtures("files")
if err != nil {
log.Fatal(err)
}

20
pkg/integrations/integrations.go

@ -23,6 +23,7 @@ import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes"
v1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"code.vikunja.io/web/handler"
"github.com/dgrijalva/jwt-go"
@ -38,34 +39,34 @@ import (
// These are the test users, the same way they are in the test database
var (
testuser1 = models.User{
testuser1 = user.User{
ID: 1,
Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
Email: "user1@example.com",
IsActive: true,
}
testuser2 = models.User{
testuser2 = user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
Email: "user2@example.com",
}
testuser3 = models.User{
testuser3 = user.User{
ID: 3,
Username: "user3",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
Email: "user3@example.com",
PasswordResetToken: "passwordresettesttoken",
}
testuser4 = models.User{
testuser4 = user.User{
ID: 4,
Username: "user4",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
Email: "user4@example.com",
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
}
testuser5 = models.User{
testuser5 = user.User{
ID: 4,
Username: "user5",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -81,7 +82,8 @@ func setupTestEnv() (e *echo.Echo, err error) {
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
// Some tests use the file engine, so we'll need to initialize that
files.InitTests()
models.SetupTests(config.ServiceRootpath.GetString())
user.InitTests()
models.SetupTests()
err = db.LoadFixtures()
if err != nil {
@ -114,7 +116,7 @@ func newTestRequest(t *testing.T, method string, handler func(ctx echo.Context)
return
}
func addUserTokenToContext(t *testing.T, user *models.User, c echo.Context) {
func addUserTokenToContext(t *testing.T, user *user.User, c echo.Context) {
// Get the token as a string
token, err := v1.NewUserJWTAuthtoken(user)
assert.NoError(t, err)
@ -152,7 +154,7 @@ func testRequestSetup(t *testing.T, method string, payload string, queryParams u
return
}
func newTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFunc, user *models.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
func newTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFunc, user *user.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
rec, c := testRequestSetup(t, method, payload, queryParams, urlParams)
addUserTokenToContext(t, user, c)
err = handler(c)
@ -185,7 +187,7 @@ func assertHandlerErrorCode(t *testing.T, err error, expectedErrorCode int) {
}
type webHandlerTest struct {
user *models.User
user *user.User
linkShare *models.LinkSharing
strFunc func() handler.CObject
t *testing.T

10
pkg/integrations/login_test.go

@ -17,8 +17,8 @@
package integrations
import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
@ -36,7 +36,7 @@ func TestLogin(t *testing.T) {
t.Run("Empty payload", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword)
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
})
t.Run("Not existing user", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{
@ -44,7 +44,7 @@ func TestLogin(t *testing.T) {
"password": "1234"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeWrongUsernameOrPassword)
assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
})
t.Run("Wrong password", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{
@ -52,7 +52,7 @@ func TestLogin(t *testing.T) {
"password": "wrong"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeWrongUsernameOrPassword)
assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
})
t.Run("user with unconfirmed email", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{
@ -60,6 +60,6 @@ func TestLogin(t *testing.T) {
"password": "1234"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeEmailNotConfirmed)
assertHandlerErrorCode(t, err, user.ErrCodeEmailNotConfirmed)
})
}

14
pkg/integrations/register_test.go

@ -17,8 +17,8 @@
package integrations
import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
@ -37,7 +37,7 @@ func TestRegister(t *testing.T) {
t.Run("Empty payload", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword)
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
})
t.Run("Empty username", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -46,7 +46,7 @@ func TestRegister(t *testing.T) {
"email": "email@example.com"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword)
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
})
t.Run("Empty password", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -55,7 +55,7 @@ func TestRegister(t *testing.T) {
"email": "email@example.com"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword)
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
})
t.Run("Empty email", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -64,7 +64,7 @@ func TestRegister(t *testing.T) {
"email": ""
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword)
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
})
t.Run("Already existing username", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -73,7 +73,7 @@ func TestRegister(t *testing.T) {
"email": "email@example.com"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrorCodeUsernameExists)
assertHandlerErrorCode(t, err, user.ErrorCodeUsernameExists)
})
t.Run("Already existing email", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -82,6 +82,6 @@ func TestRegister(t *testing.T) {
"email": "user1@example.com"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrorCodeUserEmailExists)
assertHandlerErrorCode(t, err, user.ErrorCodeUserEmailExists)
})
}

8
pkg/integrations/user_change_password_test.go

@ -17,8 +17,8 @@
package integrations
import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
@ -39,7 +39,7 @@ func TestUserChangePassword(t *testing.T) {
"old_password": "invalid"
}`, nil, nil)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeWrongUsernameOrPassword)
assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
})
t.Run("Empty old password", func(t *testing.T) {
_, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserChangePassword, &testuser1, `{
@ -47,7 +47,7 @@ func TestUserChangePassword(t *testing.T) {
"old_password": ""
}`, nil, nil)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeEmptyOldPassword)
assertHandlerErrorCode(t, err, user.ErrCodeEmptyOldPassword)
})
t.Run("Empty new password", func(t *testing.T) {
_, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserChangePassword, &testuser1, `{
@ -55,6 +55,6 @@ func TestUserChangePassword(t *testing.T) {
"old_password": "1234"
}`, nil, nil)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeEmptyNewPassword)
assertHandlerErrorCode(t, err, user.ErrCodeEmptyNewPassword)
})
}

8
pkg/integrations/user_confirm_email_test.go

@ -17,8 +17,8 @@
package integrations
import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"net/http"
@ -35,16 +35,16 @@ func TestUserConfirmEmail(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{}`)
assert.Error(t, err)
assert.Equal(t, http.StatusPreconditionFailed, err.(*echo.HTTPError).Code)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidEmailConfirmToken)
assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
})
t.Run("Empty token", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": ""}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidEmailConfirmToken)
assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
})
t.Run("Invalid token", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": "invalidToken"}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidEmailConfirmToken)
assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
})
}

6
pkg/integrations/user_password_request_token_test.go

@ -17,8 +17,8 @@
package integrations
import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"net/http"
@ -34,7 +34,7 @@ func TestUserRequestResetPasswordToken(t *testing.T) {
t.Run("Empty payload", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword)
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
})
t.Run("Invalid email address", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1example.com"}`)
@ -44,6 +44,6 @@ func TestUserRequestResetPasswordToken(t *testing.T) {
t.Run("No user with that email address", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1000@example.com"}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeUserDoesNotExist)
assertHandlerErrorCode(t, err, user.ErrCodeUserDoesNotExist)
})
}

6
pkg/integrations/user_password_reset_test.go

@ -17,8 +17,8 @@
package integrations
import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"net/http"
@ -45,7 +45,7 @@ func TestUserPasswordReset(t *testing.T) {
"token": "passwordresettesttoken"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword)
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
})
t.Run("Invalid password reset token", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserResetPassword, `{
@ -53,6 +53,6 @@ func TestUserPasswordReset(t *testing.T) {
"token": "invalidtoken"
}`)
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidPasswordResetToken)
assertHandlerErrorCode(t, err, user.ErrCodeInvalidPasswordResetToken)
})
}

4
pkg/migration/migration.go

@ -22,6 +22,8 @@ import (
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/migration"
"code.vikunja.io/api/pkg/user"
"github.com/go-xorm/xorm"
"github.com/olekukonko/tablewriter"
"os"
@ -138,5 +140,7 @@ func initSchema(tx *xorm.Engine) error {
schemeBeans := []interface{}{}
schemeBeans = append(schemeBeans, models.GetTables()...)
schemeBeans = append(schemeBeans, files.GetTables()...)
schemeBeans = append(schemeBeans, migration.GetTables()...)
schemeBeans = append(schemeBeans, user.GetTables()...)
return tx.Sync2(schemeBeans...)
}

12
pkg/models/bulk_task_test.go

@ -1,6 +1,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"testing"
)
@ -9,7 +11,7 @@ func TestBulkTask_Update(t *testing.T) {
IDs []int64
Tasks []*Task
Task Task
User *User
User *user.User
}
tests := []struct {
name string
@ -24,7 +26,7 @@ func TestBulkTask_Update(t *testing.T) {
Task: Task{
Text: "bulkupdated",
},
User: &User{ID: 1},
User: &user.User{ID: 1},
},
},
{
@ -34,7 +36,7 @@ func TestBulkTask_Update(t *testing.T) {
Task: Task{
Text: "bulkupdated",
},
User: &User{ID: 1},
User: &user.User{ID: 1},
},
wantForbidden: true,
},
@ -45,13 +47,15 @@ func TestBulkTask_Update(t *testing.T) {
Task: Task{
Text: "bulkupdated",
},
User: &User{ID: 1},
User: &user.User{ID: 1},
},
wantForbidden: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
bt := &BulkTask{
IDs: tt.fields.IDs,
Tasks: tt.fields.Tasks,

267
pkg/models/error.go

@ -46,273 +46,6 @@ func (err ErrGenericForbidden) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrorCodeGenericForbidden, Message: "You're not allowed to do this."}
}
// =====================
// User Operation Errors
// =====================
// ErrUsernameExists represents a "UsernameAlreadyExists" kind of error.
type ErrUsernameExists struct {
UserID int64
Username string
}
// IsErrUsernameExists checks if an error is a ErrUsernameExists.
func IsErrUsernameExists(err error) bool {
_, ok := err.(ErrUsernameExists)
return ok
}
func (err ErrUsernameExists) Error() string {
return fmt.Sprintf("User with that username already exists [user id: %d, username: %s]", err.UserID, err.Username)
}
// ErrorCodeUsernameExists holds the unique world-error code of this error
const ErrorCodeUsernameExists = 1001
// HTTPError holds the http error description
func (err ErrUsernameExists) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."}
}
// ErrUserEmailExists represents a "UserEmailExists" kind of error.
type ErrUserEmailExists struct {
UserID int64
Email string
}
// IsErrUserEmailExists checks if an error is a ErrUserEmailExists.
func IsErrUserEmailExists(err error) bool {
_, ok := err.(ErrUserEmailExists)
return ok
}
func (err ErrUserEmailExists) Error() string {
return fmt.Sprintf("User with that email already exists [user id: %d, email: %s]", err.UserID, err.Email)
}
// ErrorCodeUserEmailExists holds the unique world-error code of this error
const ErrorCodeUserEmailExists = 1002
// HTTPError holds the http error description
func (err ErrUserEmailExists) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."}
}
// ErrNoUsernamePassword represents a "NoUsernamePassword" kind of error.
type ErrNoUsernamePassword struct{}
// IsErrNoUsernamePassword checks if an error is a ErrNoUsernamePassword.
func IsErrNoUsernamePassword(err error) bool {
_, ok := err.(ErrNoUsernamePassword)
return ok
}
func (err ErrNoUsernamePassword) Error() string {
return fmt.Sprintf("No username and password provided")
}
// ErrCodeNoUsernamePassword holds the unique world-error code of this error
const ErrCodeNoUsernamePassword = 1004
// HTTPError holds the http error description
func (err ErrNoUsernamePassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."}
}
// ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error.
type ErrUserDoesNotExist struct {
UserID int64
}
// IsErrUserDoesNotExist checks if an error is a ErrUserDoesNotExist.
func IsErrUserDoesNotExist(err error) bool {
_, ok := err.(ErrUserDoesNotExist)
return ok
}
func (err ErrUserDoesNotExist) Error() string {
return fmt.Sprintf("User does not exist [user id: %d]", err.UserID)
}
// ErrCodeUserDoesNotExist holds the unique world-error code of this error
const ErrCodeUserDoesNotExist = 1005
// HTTPError holds the http error description
func (err ErrUserDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
}
// ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error.
type ErrCouldNotGetUserID struct{}
// IsErrCouldNotGetUserID checks if an error is a ErrCouldNotGetUserID.
func IsErrCouldNotGetUserID(err error) bool {
_, ok := err.(ErrCouldNotGetUserID)
return ok
}
func (err ErrCouldNotGetUserID) Error() string {
return fmt.Sprintf("Could not get user ID")
}
// ErrCodeCouldNotGetUserID holds the unique world-error code of this error
const ErrCodeCouldNotGetUserID = 1006
// HTTPError holds the http error description
func (err ErrCouldNotGetUserID) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
}
// ErrNoPasswordResetToken represents an error where no password reset token exists for that user
type ErrNoPasswordResetToken struct {
UserID int64
}
func (err ErrNoPasswordResetToken) Error() string {
return fmt.Sprintf("No token to reset a password [UserID: %d]", err.UserID)
}
// ErrCodeNoPasswordResetToken holds the unique world-error code of this error
const ErrCodeNoPasswordResetToken = 1008
// HTTPError holds the http error description
func (err ErrNoPasswordResetToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeNoPasswordResetToken, Message: "No token to reset a user's password provided."}
}
// ErrInvalidPasswordResetToken is an error where the password reset token is invalid
type ErrInvalidPasswordResetToken struct {
Token string
}
func (err ErrInvalidPasswordResetToken) Error() string {
return fmt.Sprintf("Invalid token to reset a password [Token: %s]", err.Token)
}
// ErrCodeInvalidPasswordResetToken holds the unique world-error code of this error
const ErrCodeInvalidPasswordResetToken = 1009
// HTTPError holds the http error description
func (err ErrInvalidPasswordResetToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidPasswordResetToken, Message: "Invalid token to reset a user's password."}
}
// IsErrInvalidPasswordResetToken checks if an error is a ErrInvalidPasswordResetToken.
func IsErrInvalidPasswordResetToken(err error) bool {
_, ok := err.(ErrInvalidPasswordResetToken)
return ok
}
// ErrInvalidEmailConfirmToken is an error where the email confirm token is invalid
type ErrInvalidEmailConfirmToken struct {
Token string
}
func (err ErrInvalidEmailConfirmToken) Error() string {
return fmt.Sprintf("Invalid email confirm token [Token: %s]", err.Token)
}
// ErrCodeInvalidEmailConfirmToken holds the unique world-error code of this error
const ErrCodeInvalidEmailConfirmToken = 1010
// HTTPError holds the http error description
func (err ErrInvalidEmailConfirmToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidEmailConfirmToken, Message: "Invalid email confirm token."}
}
// IsErrInvalidEmailConfirmToken checks if an error is a ErrInvalidEmailConfirmToken.
func IsErrInvalidEmailConfirmToken(err error) bool {
_, ok := err.(ErrInvalidEmailConfirmToken)
return ok
}
// ErrWrongUsernameOrPassword is an error where the email was not confirmed
type ErrWrongUsernameOrPassword struct {
}
func (err ErrWrongUsernameOrPassword) Error() string {
return fmt.Sprintf("Wrong username or password")
}
// ErrCodeWrongUsernameOrPassword holds the unique world-error code of this error
const ErrCodeWrongUsernameOrPassword = 1011
// HTTPError holds the http error description
func (err ErrWrongUsernameOrPassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeWrongUsernameOrPassword, Message: "Wrong username or password."}
}
// IsErrWrongUsernameOrPassword checks if an error is a IsErrEmailNotConfirmed.
func IsErrWrongUsernameOrPassword(err error) bool {
_, ok := err.(ErrWrongUsernameOrPassword)
return ok
}
// ErrEmailNotConfirmed is an error where the email was not confirmed
type ErrEmailNotConfirmed struct {
UserID int64
}
func (err ErrEmailNotConfirmed) Error() string {
return fmt.Sprintf("Email is not confirmed [UserID: %d]", err.UserID)
}
// ErrCodeEmailNotConfirmed holds the unique world-error code of this error
const ErrCodeEmailNotConfirmed = 1012
// HTTPError holds the http error description
func (err ErrEmailNotConfirmed) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmailNotConfirmed, Message: "Please confirm your email address."}
}
// IsErrEmailNotConfirmed checks if an error is a IsErrEmailNotConfirmed.
func IsErrEmailNotConfirmed(err error) bool {
_, ok := err.(ErrEmailNotConfirmed)
return ok
}
// ErrEmptyNewPassword represents a "EmptyNewPassword" kind of error.
type ErrEmptyNewPassword struct{}
// IsErrEmptyNewPassword checks if an error is a ErrEmptyNewPassword.
func IsErrEmptyNewPassword(err error) bool {
_, ok := err.(ErrEmptyNewPassword)
return ok
}
func (err ErrEmptyNewPassword) Error() string {
return fmt.Sprintf("New password is empty")
}
// ErrCodeEmptyNewPassword holds the unique world-error code of this error
const ErrCodeEmptyNewPassword = 1013
// HTTPError holds the http error description
func (err ErrEmptyNewPassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmptyNewPassword, Message: "Please specify new password."}
}
// ErrEmptyOldPassword represents a "EmptyOldPassword" kind of error.
type ErrEmptyOldPassword struct{}
// IsErrEmptyOldPassword checks if an error is a ErrEmptyOldPassword.
func IsErrEmptyOldPassword(err error) bool {
_, ok := err.(ErrEmptyOldPassword)
return ok
}
func (err ErrEmptyOldPassword) Error() string {
return fmt.Sprintf("Old password is empty")
}
// ErrCodeEmptyOldPassword holds the unique world-error code of this error
const ErrCodeEmptyOldPassword = 1014
// HTTPError holds the http error description
func (err ErrEmptyOldPassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmptyOldPassword, Message: "Please specify old password."}
}
// ===================
// Empty things errors
// ===================

1
pkg/models/fixtures/files.yml

@ -1 +0,0 @@
../../files/fixtures/files.yml

11
pkg/models/label.go

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"time"
)
@ -34,7 +35,7 @@ type Label struct {
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
// The user who created this label
CreatedBy *User `xorm:"-" json:"created_by"`
CreatedBy *user.User `xorm:"-" json:"created_by"`
// A unix timestamp when this label was created. You cannot change this value.
Created int64 `xorm:"created not null" json:"created"`
@ -63,7 +64,7 @@ func (Label) TableName() string {
// @Failure 500 {object} models.Message "Internal error"
// @Router /labels [put]
func (l *Label) Create(a web.Auth) (err error) {
u, err := getUserWithError(a)
u, err := user.GetFromAuth(a)
if err != nil {
return
}
@ -136,7 +137,7 @@ func (l *Label) ReadAll(a web.Auth, search string, page int, perPage int) (ls in
return nil, 0, 0, ErrGenericForbidden{}
}
u := &User{ID: a.GetID()}
u := &user.User{ID: a.GetID()}
// Get all tasks
taskIDs, err := getUserTaskIDs(u)
@ -175,7 +176,7 @@ func (l *Label) ReadOne() (err error) {
}
*l = *label
user, err := GetUserByID(l.CreatedByID)
user, err := user.GetUserByID(l.CreatedByID)
if err != nil {
return err
}
@ -198,7 +199,7 @@ func getLabelByIDSimple(labelID int64) (*Label, error) {
}
// Helper method to get all task ids a user has
func getUserTaskIDs(u *User) (taskIDs []int64, err error) {
func getUserTaskIDs(u *user.User) (taskIDs []int64, err error) {
// Get all lists
lists, _, _, err := getRawListsForUser("", u, -1, 0)

3
pkg/models/label_rights.go

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/go-xorm/builder"
)
@ -65,7 +66,7 @@ func (l *Label) hasAccessToLabel(a web.Auth) (bool, error) {
// TODO: add an extra check for link share handling
// Get all tasks
taskIDs, err := getUserTaskIDs(&User{ID: a.GetID()})
taskIDs, err := getUserTaskIDs(&user.User{ID: a.GetID()})
if err != nil {
return false, err
}

9
pkg/models/label_task.go

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/go-xorm/builder"
)
@ -120,7 +121,7 @@ func (lt *LabelTask) ReadAll(a web.Auth, search string, page int, perPage int) (
}
return getLabelsByTaskIDs(&LabelByTaskIDsOptions{
User: &User{ID: a.GetID()},
User: &user.User{ID: a.GetID()},
Search: search,
Page: page,
TaskIDs: []int64{lt.TaskID},
@ -135,7 +136,7 @@ type labelWithTaskID struct {
// LabelByTaskIDsOptions is a struct to not clutter the function with too many optional parameters.
type LabelByTaskIDsOptions struct {
User *User
User *user.User
Search string
Page int
PerPage int
@ -185,7 +186,7 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
for _, l := range labels {
userids = append(userids, l.CreatedByID)
}
users := make(map[int64]*User)
users := make(map[int64]*user.User)
err = x.In("id", userids).Find(&users)
if err != nil {
return nil, 0, 0, err
@ -290,7 +291,7 @@ func (t *Task) updateTaskLabels(creator web.Auth, labels []*Label) (err error) {
return err
}
if !hasAccessToLabel {
user, _ := creator.(*User)
user, _ := creator.(*user.User)
return ErrUserHasNoAccessToLabel{LabelID: l.ID, UserID: user.ID}
}

38
pkg/models/label_task_test.go

@ -1,6 +1,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"gopkg.in/d4l3k/messagediff.v1"
"reflect"
"runtime"
@ -37,7 +39,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
TaskID: 1,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantLabels: []*labelWithTaskID{
{
@ -46,7 +48,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
ID: 4,
Title: "Label #4 - visible via other task",
CreatedByID: 2,
CreatedBy: &User{
CreatedBy: &user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -62,7 +64,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
TaskID: 14,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantErr: true,
errType: IsErrNoRightToSeeTask,
@ -73,7 +75,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
TaskID: 9999,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantErr: true,
errType: IsErrTaskDoesNotExist,
@ -81,6 +83,8 @@ func TestLabelTask_ReadAll(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
l := &LabelTask{
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
@ -131,17 +135,17 @@ func TestLabelTask_Create(t *testing.T) {
LabelID: 1,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
},
{
name: "already existing",
fields: fields{
TaskID: 1,
LabelID: 1,
LabelID: 4,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantErr: true,
errType: IsErrLabelIsAlreadyOnTask,
@ -153,7 +157,7 @@ func TestLabelTask_Create(t *testing.T) {
LabelID: 9999,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantForbidden: true,
},
@ -164,7 +168,7 @@ func TestLabelTask_Create(t *testing.T) {
LabelID: 1,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantForbidden: true,
wantErr: true,
@ -173,6 +177,8 @@ func TestLabelTask_Create(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
l := &LabelTask{
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
@ -217,9 +223,9 @@ func TestLabelTask_Delete(t *testing.T) {
name: "normal",
fields: fields{
TaskID: 1,
LabelID: 1,
LabelID: 4,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
},
{
name: "delete nonexistant",
@ -227,7 +233,7 @@ func TestLabelTask_Delete(t *testing.T) {
TaskID: 1,
LabelID: 1,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
{
@ -236,7 +242,7 @@ func TestLabelTask_Delete(t *testing.T) {
TaskID: 1,
LabelID: 9999,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
{
@ -245,7 +251,7 @@ func TestLabelTask_Delete(t *testing.T) {