diff --git a/README.md b/README.md
index 2d0e314cf..ed998fab6 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@
[](https://hub.docker.com/r/vikunja/api/)
[](https://try.vikunja.io/api/v1/swagger)
[](https://goreportcard.com/report/git.kolaente.de/vikunja/api)
+[](https://cover.run/go?tag=golang-1.10&repo=code.vikunja.io%2Fapi)
## Features
diff --git a/go.sum b/go.sum
index 4d1516f72..cdab07c98 100644
--- a/go.sum
+++ b/go.sum
@@ -18,6 +18,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cweill/gotests v1.5.2 h1:kKqmKmS2wCV3tuLnfpbiuN8OlkosQZTpCfiqmiuNAsA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20180901172138-1eb28afdf9b6 h1:BZGp1dbKFjqlGmxEpwkDpCWNxVwEYnUPoncIzLiHlPo=
diff --git a/pkg/caldav/caldav.go b/pkg/caldav/caldav.go
index cef5d775e..a7f11a981 100644
--- a/pkg/caldav/caldav.go
+++ b/pkg/caldav/caldav.go
@@ -93,7 +93,8 @@ END:VCALENDAR` // Need a line break
}
func makeCalDavTimeFromUnixTime(unixtime int64) (caldavtime string) {
- tm := time.Unix(unixtime, 0)
+ tz, _ := time.LoadLocation("UTC")
+ tm := time.Unix(unixtime, 0).In(tz)
return tm.Format("20060102T150405")
}
diff --git a/pkg/caldav/caldav_test.go b/pkg/caldav/caldav_test.go
new file mode 100644
index 000000000..59badd761
--- /dev/null
+++ b/pkg/caldav/caldav_test.go
@@ -0,0 +1,246 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2018 Vikunja and contributors. All rights reserved.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+package caldav
+
+import "testing"
+
+func TestParseEvents(t *testing.T) {
+ type args struct {
+ config *Config
+ events []*Event
+ }
+ tests := []struct {
+ name string
+ args args
+ wantCaldavevents string
+ }{
+ {
+ name: "Test caldavparsing without reminders",
+ args: args{
+ config: &Config{
+ Name: "test",
+ ProdID: "RandomProdID which is not random",
+ },
+ events: []*Event{
+ {
+ Summary: "Event #1",
+ Description: "Lorem Ipsum",
+ UID: "randommduid",
+ TimestampUnix: 1543626724,
+ StartUnix: 1543626724,
+ EndUnix: 1543627824,
+ },
+ {
+ Summary: "Event #2",
+ UID: "randommduidd",
+ TimestampUnix: 1543726724,
+ StartUnix: 1543726724,
+ EndUnix: 1543738724,
+ },
+ {
+ Summary: "Event #3 with empty uid",
+ UID: "20181202T0600242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83",
+ TimestampUnix: 1543726824,
+ StartUnix: 1543726824,
+ EndUnix: 1543727000,
+ },
+ },
+ },
+ wantCaldavevents: `BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:PUBLISH
+X-PUBLISHED-TTL:PT4H
+X-WR-CALNAME:test
+PRODID:-//RandomProdID which is not random//EN
+BEGIN:VEVENT
+UID:randommduid
+SUMMARY:Event #1
+DESCRIPTION:Lorem Ipsum
+DTSTAMP:20181201T011204
+DTSTART:20181201T011204
+DTEND:20181201T013024
+END:VEVENT
+BEGIN:VEVENT
+UID:randommduidd
+SUMMARY:Event #2
+DESCRIPTION:
+DTSTAMP:20181202T045844
+DTSTART:20181202T045844
+DTEND:20181202T081844
+END:VEVENT
+BEGIN:VEVENT
+UID:20181202T0600242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83
+SUMMARY:Event #3 with empty uid
+DESCRIPTION:
+DTSTAMP:20181202T050024
+DTSTART:20181202T050024
+DTEND:20181202T050320
+END:VEVENT
+END:VCALENDAR`,
+ },
+ {
+ name: "Test caldavparsing with reminders",
+ args: args{
+ config: &Config{
+ Name: "test2",
+ ProdID: "RandomProdID which is not random",
+ },
+ events: []*Event{
+ {
+ Summary: "Event #1",
+ Description: "Lorem Ipsum",
+ UID: "randommduid",
+ TimestampUnix: 1543626724,
+ StartUnix: 1543626724,
+ EndUnix: 1543627824,
+ Alarms: []Alarm{
+ {TimeUnix: 1543626524},
+ {TimeUnix: 1543626224},
+ {TimeUnix: 1543626024},
+ },
+ },
+ {
+ Summary: "Event #2",
+ UID: "randommduidd",
+ TimestampUnix: 1543726724,
+ StartUnix: 1543726724,
+ EndUnix: 1543738724,
+ Alarms: []Alarm{
+ {TimeUnix: 1543626524},
+ {TimeUnix: 1543626224},
+ {TimeUnix: 1543626024},
+ },
+ },
+ {
+ Summary: "Event #3 with empty uid",
+ TimestampUnix: 1543726824,
+ StartUnix: 1543726824,
+ EndUnix: 1543727000,
+ Alarms: []Alarm{
+ {TimeUnix: 1543626524},
+ {TimeUnix: 1543626224},
+ {TimeUnix: 1543626024},
+ {TimeUnix: 1543826824},
+ },
+ },
+ {
+ Summary: "Event #4 without any",
+ TimestampUnix: 1543726824,
+ StartUnix: 1543726824,
+ EndUnix: 1543727000,
+ },
+ },
+ },
+ wantCaldavevents: `BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:PUBLISH
+X-PUBLISHED-TTL:PT4H
+X-WR-CALNAME:test2
+PRODID:-//RandomProdID which is not random//EN
+BEGIN:VEVENT
+UID:randommduid
+SUMMARY:Event #1
+DESCRIPTION:Lorem Ipsum
+DTSTAMP:20181201T011204
+DTSTART:20181201T011204
+DTEND:20181201T013024
+BEGIN:VALARM
+TRIGGER:-PT3M
+ACTION:DISPLAY
+DESCRIPTION:Event #1
+END:VALARM
+BEGIN:VALARM
+TRIGGER:-PT8M
+ACTION:DISPLAY
+DESCRIPTION:Event #1
+END:VALARM
+BEGIN:VALARM
+TRIGGER:-PT11M
+ACTION:DISPLAY
+DESCRIPTION:Event #1
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:randommduidd
+SUMMARY:Event #2
+DESCRIPTION:
+DTSTAMP:20181202T045844
+DTSTART:20181202T045844
+DTEND:20181202T081844
+BEGIN:VALARM
+TRIGGER:-PT1670M
+ACTION:DISPLAY
+DESCRIPTION:Event #2
+END:VALARM
+BEGIN:VALARM
+TRIGGER:-PT1675M
+ACTION:DISPLAY
+DESCRIPTION:Event #2
+END:VALARM
+BEGIN:VALARM
+TRIGGER:-PT1678M
+ACTION:DISPLAY
+DESCRIPTION:Event #2
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:20181202T0500242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83
+SUMMARY:Event #3 with empty uid
+DESCRIPTION:
+DTSTAMP:20181202T050024
+DTSTART:20181202T050024
+DTEND:20181202T050320
+BEGIN:VALARM
+TRIGGER:-PT1671M
+ACTION:DISPLAY
+DESCRIPTION:Event #3 with empty uid
+END:VALARM
+BEGIN:VALARM
+TRIGGER:-PT1676M
+ACTION:DISPLAY
+DESCRIPTION:Event #3 with empty uid
+END:VALARM
+BEGIN:VALARM
+TRIGGER:-PT1680M
+ACTION:DISPLAY
+DESCRIPTION:Event #3 with empty uid
+END:VALARM
+BEGIN:VALARM
+TRIGGER:PT1666M
+ACTION:DISPLAY
+DESCRIPTION:Event #3 with empty uid
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:20181202T050024ae7548ce9556df85038abe90dc674d4741a61ce74d1cf
+SUMMARY:Event #4 without any
+DESCRIPTION:
+DTSTAMP:20181202T050024
+DTSTART:20181202T050024
+DTEND:20181202T050320
+END:VEVENT
+END:VCALENDAR`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if gotCaldavevents := ParseEvents(tt.args.config, tt.args.events); gotCaldavevents != tt.wantCaldavevents {
+ t.Errorf("ParseEvents() = %v, want %v", gotCaldavevents, tt.wantCaldavevents)
+ }
+ })
+ }
+}
diff --git a/pkg/models/fixtures/list.yml b/pkg/models/fixtures/list.yml
index c151e2fc1..27fbd05bd 100644
--- a/pkg/models/fixtures/list.yml
+++ b/pkg/models/fixtures/list.yml
@@ -15,4 +15,10 @@
title: Test3
description: Lorem Ipsum
owner_id: 3
- namespace_id: 2
\ No newline at end of file
+ namespace_id: 2
+-
+ id: 4
+ title: Test4
+ description: Lorem Ipsum
+ owner_id: 3
+ namespace_id: 3
\ No newline at end of file
diff --git a/pkg/models/fixtures/namespaces.yml b/pkg/models/fixtures/namespaces.yml
index 12c191306..11042eacf 100644
--- a/pkg/models/fixtures/namespaces.yml
+++ b/pkg/models/fixtures/namespaces.yml
@@ -7,4 +7,9 @@
id: 2
name: testnamespace2
description: Lorem Ipsum
- owner_id: 2
\ No newline at end of file
+ owner_id: 2
+-
+ id: 3
+ name: testnamespace3
+ description: Lorem Ipsum
+ owner_id: 3
\ No newline at end of file
diff --git a/pkg/models/fixtures/tasks.yml b/pkg/models/fixtures/tasks.yml
new file mode 100644
index 000000000..03578a5f9
--- /dev/null
+++ b/pkg/models/fixtures/tasks.yml
@@ -0,0 +1,41 @@
+- id: 1
+ text: 'task #1'
+ created_by_id: 1
+ list_id: 1
+ created: 1543626724
+ updated: 1543626724
+- id: 2
+ text: 'task #2 done'
+ done: true
+ created_by_id: 1
+ list_id: 1
+ created: 1543626724
+ updated: 1543626724
+- id: 3
+ text: 'task #3 high prio'
+ created_by_id: 1
+ list_id: 1
+ created: 1543626724
+ updated: 1543626724
+ priority: 100
+- id: 4
+ text: 'task #4 low prio'
+ created_by_id: 1
+ list_id: 1
+ created: 1543626724
+ updated: 1543626724
+ priority: 1
+- id: 5
+ text: 'task #5 higher due date'
+ created_by_id: 1
+ list_id: 1
+ created: 1543626724
+ updated: 1543626724
+ due_date_unix: 1543636724
+- id: 6
+ text: 'task #6 lower due date'
+ created_by_id: 1
+ list_id: 1
+ created: 1543626724
+ updated: 1543626724
+ due_date_unix: 1543616724
\ No newline at end of file
diff --git a/pkg/models/fixtures/team_list.yml b/pkg/models/fixtures/team_list.yml
new file mode 100644
index 000000000..9cf742f01
--- /dev/null
+++ b/pkg/models/fixtures/team_list.yml
@@ -0,0 +1,6 @@
+- id: 1
+ team_id: 1
+ list_id: 3
+- id: 2
+ team_id: 2
+ list_id: 3
diff --git a/pkg/models/fixtures/team_namespaces.yml b/pkg/models/fixtures/team_namespaces.yml
new file mode 100644
index 000000000..413d49534
--- /dev/null
+++ b/pkg/models/fixtures/team_namespaces.yml
@@ -0,0 +1,6 @@
+- id: 1
+ team_id: 1
+ namespace_id: 3
+- id: 2
+ team_id: 2
+ namespace_id: 3
diff --git a/pkg/models/fixtures/users.yml b/pkg/models/fixtures/users.yml
index e278cd49f..46be8c211 100644
--- a/pkg/models/fixtures/users.yml
+++ b/pkg/models/fixtures/users.yml
@@ -13,3 +13,9 @@
username: 'user3'
password: '1234'
email: 'johndoe@example.com'
+-
+ id: 4
+ username: 'user4'
+ password: '1234'
+ email: 'johndoe@example.com'
+ email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
\ No newline at end of file
diff --git a/pkg/models/fixtures/users_list.yml b/pkg/models/fixtures/users_list.yml
new file mode 100644
index 000000000..7d56be8fe
--- /dev/null
+++ b/pkg/models/fixtures/users_list.yml
@@ -0,0 +1,6 @@
+- id: 1
+ user_id: 1
+ list_id: 3
+- id: 2
+ user_id: 2
+ list_id: 3
diff --git a/pkg/models/fixtures/users_namespace.yml b/pkg/models/fixtures/users_namespace.yml
new file mode 100644
index 000000000..7212e0dbd
--- /dev/null
+++ b/pkg/models/fixtures/users_namespace.yml
@@ -0,0 +1,6 @@
+- id: 1
+ user_id: 1
+ namespace_id: 3
+- id: 2
+ user_id: 2
+ namespace_id: 3
diff --git a/pkg/models/list_read_test.go b/pkg/models/list_read_test.go
index c0f7b2ccd..823ebbde1 100644
--- a/pkg/models/list_read_test.go
+++ b/pkg/models/list_read_test.go
@@ -40,7 +40,7 @@ func TestList_ReadAll(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, reflect.TypeOf(lists3).Kind(), reflect.Slice)
s := reflect.ValueOf(lists3)
- assert.Equal(t, s.Len(), 1)
+ assert.Equal(t, s.Len(), 3)
// Try getting lists for a nonexistant user
_, err = lists2.ReadAll("", &User{ID: 984234}, 1)
diff --git a/pkg/models/list_rights.go b/pkg/models/list_rights.go
index e560b147e..772752137 100644
--- a/pkg/models/list_rights.go
+++ b/pkg/models/list_rights.go
@@ -26,7 +26,7 @@ func (l *List) IsAdmin(a web.Auth) bool {
u := getUserForRights(a)
// Owners are always admins
- if l.Owner.ID == u.ID {
+ if l.OwnerID == u.ID {
return true
}
diff --git a/pkg/models/list_task_readall_test.go b/pkg/models/list_task_readall_test.go
new file mode 100644
index 000000000..8ada56818
--- /dev/null
+++ b/pkg/models/list_task_readall_test.go
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2018 the Vikunja Authors. All rights reserved.
+ * Use of this source code is governed by a LPGLv3-style
+ * license that can be found in the LICENSE file.
+ */
+
+package models
+
+import (
+ "reflect"
+ "sort"
+ "testing"
+
+ "code.vikunja.io/web"
+)
+
+func sortTasksForTesting(by SortBy) (tasks []*ListTask) {
+ tasks = []*ListTask{
+ {
+ ID: 1,
+ Text: "task #1",
+ CreatedByID: 1,
+ ListID: 1,
+ Created: 1543626724,
+ Updated: 1543626724,
+ },
+ {
+ ID: 2,
+ Text: "task #2 done",
+ Done: true,
+ CreatedByID: 1,
+ ListID: 1,
+ Created: 1543626724,
+ Updated: 1543626724,
+ },
+ {
+ ID: 3,
+ Text: "task #3 high prio",
+ CreatedByID: 1,
+ ListID: 1,
+ Created: 1543626724,
+ Updated: 1543626724,
+ Priority: 100,
+ },
+ {
+ ID: 4,
+ Text: "task #4 low prio",
+ CreatedByID: 1,
+ ListID: 1,
+ Created: 1543626724,
+ Updated: 1543626724,
+ Priority: 1,
+ },
+ {
+ ID: 5,
+ Text: "task #5 higher due date",
+ CreatedByID: 1,
+ ListID: 1,
+ Created: 1543626724,
+ Updated: 1543626724,
+ DueDateUnix: 1543636724,
+ },
+ {
+ ID: 6,
+ Text: "task #6 lower due date",
+ CreatedByID: 1,
+ ListID: 1,
+ Created: 1543626724,
+ Updated: 1543626724,
+ DueDateUnix: 1543616724,
+ },
+ }
+
+ switch by {
+ case SortTasksByPriorityDesc:
+ sort.Slice(tasks, func(i, j int) bool {
+ return tasks[i].Priority > tasks[j].Priority
+ })
+ case SortTasksByPriorityAsc:
+ sort.Slice(tasks, func(i, j int) bool {
+ return tasks[i].Priority < tasks[j].Priority
+ })
+ case SortTasksByDueDateDesc:
+ sort.Slice(tasks, func(i, j int) bool {
+ return tasks[i].DueDateUnix > tasks[j].DueDateUnix
+ })
+ case SortTasksByDueDateAsc:
+ sort.Slice(tasks, func(i, j int) bool {
+ return tasks[i].DueDateUnix < tasks[j].DueDateUnix
+ })
+ }
+
+ return
+}
+
+func TestListTask_ReadAll(t *testing.T) {
+ type fields struct {
+ ID int64
+ Text string
+ Description string
+ Done bool
+ DueDateUnix int64
+ RemindersUnix []int64
+ CreatedByID int64
+ ListID int64
+ RepeatAfter int64
+ ParentTaskID int64
+ Priority int64
+ Sorting string
+ Subtasks []*ListTask
+ Created int64
+ Updated int64
+ CreatedBy User
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ search string
+ a web.Auth
+ page int
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want interface{}
+ wantErr bool
+ }{
+ {
+ name: "ReadAll ListTasks normally",
+ fields: fields{},
+ args: args{
+ search: "",
+ a: &User{ID: 1},
+ page: 0,
+ },
+ want: sortTasksForTesting(SortTasksByUnsorted),
+ wantErr: false,
+ },
+ {
+ name: "ReadAll ListTasks sorted by priority (desc)",
+ fields: fields{
+ Sorting: "priority",
+ },
+ args: args{
+ search: "",
+ a: &User{ID: 1},
+ page: 0,
+ },
+ want: sortTasksForTesting(SortTasksByPriorityDesc),
+ wantErr: false,
+ },
+ {
+ name: "ReadAll ListTasks sorted by priority asc",
+ fields: fields{
+ Sorting: "priorityasc",
+ },
+ args: args{
+ search: "",
+ a: &User{ID: 1},
+ page: 0,
+ },
+ want: sortTasksForTesting(SortTasksByPriorityAsc),
+ wantErr: false,
+ },
+ {
+ name: "ReadAll ListTasks sorted by priority desc",
+ fields: fields{
+ Sorting: "prioritydesc",
+ },
+ args: args{
+ search: "",
+ a: &User{ID: 1},
+ page: 0,
+ },
+ want: sortTasksForTesting(SortTasksByPriorityDesc),
+ wantErr: false,
+ },
+ {
+ name: "ReadAll ListTasks sorted by due date default (desc)",
+ fields: fields{
+ Sorting: "dueadate",
+ },
+ args: args{
+ search: "",
+ a: &User{ID: 1},
+ page: 0,
+ },
+ want: sortTasksForTesting(SortTasksByDueDateDesc),
+ wantErr: false,
+ },
+ {
+ name: "ReadAll ListTasks sorted by due date asc",
+ fields: fields{
+ Sorting: "duedateasc",
+ },
+ args: args{
+ search: "",
+ a: &User{ID: 1},
+ page: 0,
+ },
+ want: sortTasksForTesting(SortTasksByDueDateAsc),
+ wantErr: false,
+ },
+ {
+ name: "ReadAll ListTasks sorted by due date desc",
+ fields: fields{
+ Sorting: "dueadatedesc",
+ },
+ args: args{
+ search: "",
+ a: &User{ID: 1},
+ page: 0,
+ },
+ want: sortTasksForTesting(SortTasksByDueDateDesc),
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ lt := &ListTask{
+ ID: tt.fields.ID,
+ Text: tt.fields.Text,
+ Description: tt.fields.Description,
+ Done: tt.fields.Done,
+ DueDateUnix: tt.fields.DueDateUnix,
+ RemindersUnix: tt.fields.RemindersUnix,
+ CreatedByID: tt.fields.CreatedByID,
+ ListID: tt.fields.ListID,
+ RepeatAfter: tt.fields.RepeatAfter,
+ ParentTaskID: tt.fields.ParentTaskID,
+ Priority: tt.fields.Priority,
+ Sorting: tt.fields.Sorting,
+ Subtasks: tt.fields.Subtasks,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CreatedBy: tt.fields.CreatedBy,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ got, err := lt.ReadAll(tt.args.search, tt.args.a, tt.args.page)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ListTask.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ListTask.ReadAll() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/pkg/models/list_users_readall.go b/pkg/models/list_users_readall.go
index 6cbaa62d3..04914ed48 100644
--- a/pkg/models/list_users_readall.go
+++ b/pkg/models/list_users_readall.go
@@ -44,7 +44,7 @@ func (ul *ListUser) ReadAll(search string, a web.Auth, page int) (interface{}, e
return nil, err
}
if !l.CanRead(u) {
- return nil, ErrNeedToHaveListReadAccess{}
+ return nil, ErrNeedToHaveListReadAccess{UserID: u.ID, ListID: ul.ListID}
}
// Get all users
diff --git a/pkg/models/list_users_rights_test.go b/pkg/models/list_users_rights_test.go
new file mode 100644
index 000000000..96f3bfa1a
--- /dev/null
+++ b/pkg/models/list_users_rights_test.go
@@ -0,0 +1,99 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2018 Vikunja and contributors. All rights reserved.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+package models
+
+import (
+ "testing"
+
+ "code.vikunja.io/web"
+)
+
+func TestListUser_CanDoSomething(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ ListID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ a web.Auth
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want map[string]bool
+ }{
+ {
+ name: "CanDoSomething Normally",
+ fields: fields{
+ ListID: 3,
+ },
+ args: args{
+ a: &User{ID: 3},
+ },
+ want: map[string]bool{"CanCreate": true, "CanDelete": true, "CanUpdate": true},
+ },
+ {
+ name: "CanDoSomething for a nonexistant list",
+ fields: fields{
+ ListID: 300,
+ },
+ args: args{
+ a: &User{ID: 3},
+ },
+ want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
+ },
+ {
+ name: "CanDoSomething where the user does not have the rights",
+ fields: fields{
+ ListID: 3,
+ },
+ args: args{
+ a: &User{ID: 4},
+ },
+ want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ lu := &ListUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ ListID: tt.fields.ListID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ if got := lu.CanCreate(tt.args.a); got != tt.want["CanCreate"] {
+ t.Errorf("ListUser.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
+ }
+ if got := lu.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
+ t.Errorf("ListUser.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
+ }
+ if got := lu.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
+ t.Errorf("ListUser.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
+ }
+ })
+ }
+}
diff --git a/pkg/models/list_users_test.go b/pkg/models/list_users_test.go
new file mode 100644
index 000000000..f1cff13c6
--- /dev/null
+++ b/pkg/models/list_users_test.go
@@ -0,0 +1,355 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2018 Vikunja and contributors. All rights reserved.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+package models
+
+import (
+ "reflect"
+ "runtime"
+ "testing"
+
+ "code.vikunja.io/web"
+)
+
+func TestListUser_Create(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ ListID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ a web.Auth
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "ListUsers Create normally",
+ fields: fields{
+ UserID: 1,
+ ListID: 2,
+ },
+ },
+ {
+ name: "ListUsers Create for duplicate",
+ fields: fields{
+ UserID: 1,
+ ListID: 2,
+ },
+ wantErr: true,
+ errType: IsErrUserAlreadyHasAccess,
+ },
+ {
+ name: "ListUsers Create with invalid right",
+ fields: fields{
+ UserID: 1,
+ ListID: 2,
+ Right: 500,
+ },
+ wantErr: true,
+ errType: IsErrInvalidUserRight,
+ },
+ {
+ name: "ListUsers Create with inexisting list",
+ fields: fields{
+ UserID: 1,
+ ListID: 2000,
+ },
+ wantErr: true,
+ errType: IsErrListDoesNotExist,
+ },
+ {
+ name: "ListUsers Create with inexisting user",
+ fields: fields{
+ UserID: 500,
+ ListID: 2,
+ },
+ wantErr: true,
+ errType: IsErrUserDoesNotExist,
+ },
+ {
+ name: "ListUsers Create with the owner as shared user",
+ fields: fields{
+ UserID: 1,
+ ListID: 1,
+ },
+ wantErr: true,
+ errType: IsErrUserAlreadyHasAccess,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ul := &ListUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ ListID: tt.fields.ListID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ err := ul.Create(tt.args.a)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ListUser.Create() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("ListUser.Create() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ })
+ }
+}
+
+func TestListUser_ReadAll(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ ListID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ search string
+ a web.Auth
+ page int
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want interface{}
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "Test readall normal",
+ fields: fields{
+ ListID: 3,
+ },
+ args: args{
+ a: &User{ID: 3},
+ },
+ want: []*UserWithRight{
+ {
+ User: User{
+ ID: 1,
+ Username: "user1",
+ Password: "1234",
+ Email: "johndoe@example.com",
+ },
+ Right: UserRightRead,
+ },
+ {
+ User: User{
+ ID: 2,
+ Username: "user2",
+ Password: "1234",
+ Email: "johndoe@example.com",
+ },
+ Right: UserRightRead,
+ },
+ },
+ },
+ {
+ name: "Test ReadAll by a user who does not have access to the list",
+ fields: fields{
+ ListID: 3,
+ },
+ args: args{
+ a: &User{ID: 4},
+ },
+ wantErr: true,
+ errType: IsErrNeedToHaveListReadAccess,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ul := &ListUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ ListID: tt.fields.ListID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ got, err := ul.ReadAll(tt.args.search, tt.args.a, tt.args.page)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ListUser.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("ListUser.ReadAll() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ListUser.ReadAll() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestListUser_Update(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ ListID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ tests := []struct {
+ name string
+ fields fields
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "Test Update Normally",
+ fields: fields{
+ ListID: 3,
+ UserID: 1,
+ Right: UserRightAdmin,
+ },
+ },
+ {
+ name: "Test Update to write",
+ fields: fields{
+ ListID: 3,
+ UserID: 1,
+ Right: UserRightWrite,
+ },
+ },
+ {
+ name: "Test Update to Read",
+ fields: fields{
+ ListID: 3,
+ UserID: 1,
+ Right: UserRightRead,
+ },
+ },
+ {
+ name: "Test Update with invalid right",
+ fields: fields{
+ ListID: 3,
+ UserID: 1,
+ Right: 500,
+ },
+ wantErr: true,
+ errType: IsErrInvalidUserRight,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ lu := &ListUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ ListID: tt.fields.ListID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ err := lu.Update()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ListUser.Update() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("ListUser.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ })
+ }
+}
+
+func TestListUser_Delete(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ ListID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ tests := []struct {
+ name string
+ fields fields
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "Try deleting some unexistant user",
+ fields: fields{
+ UserID: 1000,
+ ListID: 2,
+ },
+ wantErr: true,
+ errType: IsErrUserDoesNotExist,
+ },
+ {
+ name: "Try deleting a user which does not has access but exists",
+ fields: fields{
+ UserID: 1,
+ ListID: 4,
+ },
+ wantErr: true,
+ errType: IsErrUserDoesNotHaveAccessToList,
+ },
+ {
+ name: "Try deleting normally",
+ fields: fields{
+ UserID: 1,
+ ListID: 3,
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ lu := &ListUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ ListID: tt.fields.ListID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ err := lu.Delete()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ListUser.Delete() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("ListUser.Delete() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ })
+ }
+}
diff --git a/pkg/models/namespace_test.go b/pkg/models/namespace_test.go
index a06e84134..f224df797 100644
--- a/pkg/models/namespace_test.go
+++ b/pkg/models/namespace_test.go
@@ -105,5 +105,5 @@ func TestNamespace_Create(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, reflect.TypeOf(nsps).Kind(), reflect.Slice)
s := reflect.ValueOf(nsps)
- assert.Equal(t, 1, s.Len())
+ assert.Equal(t, 3, s.Len())
}
diff --git a/pkg/models/namespace_users_rights_test.go b/pkg/models/namespace_users_rights_test.go
new file mode 100644
index 000000000..8883b67a0
--- /dev/null
+++ b/pkg/models/namespace_users_rights_test.go
@@ -0,0 +1,99 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2018 Vikunja and contributors. All rights reserved.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+package models
+
+import (
+ "testing"
+
+ "code.vikunja.io/web"
+)
+
+func TestNamespaceUser_CanDoSomething(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ NamespaceID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ a web.Auth
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want map[string]bool
+ }{
+ {
+ name: "CanDoSomething Normally",
+ fields: fields{
+ NamespaceID: 3,
+ },
+ args: args{
+ a: &User{ID: 3},
+ },
+ want: map[string]bool{"CanCreate": true, "CanDelete": true, "CanUpdate": true},
+ },
+ {
+ name: "CanDoSomething for a nonexistant namespace",
+ fields: fields{
+ NamespaceID: 300,
+ },
+ args: args{
+ a: &User{ID: 3},
+ },
+ want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
+ },
+ {
+ name: "CanDoSomething where the user does not have the rights",
+ fields: fields{
+ NamespaceID: 3,
+ },
+ args: args{
+ a: &User{ID: 4},
+ },
+ want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ nu := &NamespaceUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ NamespaceID: tt.fields.NamespaceID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ if got := nu.CanCreate(tt.args.a); got != tt.want["CanCreate"] {
+ t.Errorf("NamespaceUser.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
+ }
+ if got := nu.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
+ t.Errorf("NamespaceUser.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
+ }
+ if got := nu.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
+ t.Errorf("NamespaceUser.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
+ }
+ })
+ }
+}
diff --git a/pkg/models/namespace_users_test.go b/pkg/models/namespace_users_test.go
new file mode 100644
index 000000000..16b659d88
--- /dev/null
+++ b/pkg/models/namespace_users_test.go
@@ -0,0 +1,357 @@
+/*
+ * Vikunja is a todo-list application to facilitate your life.
+ * Copyright 2018 Vikunja and contributors. All rights reserved.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+package models
+
+import (
+ "code.vikunja.io/web"
+ "reflect"
+ "runtime"
+ "testing"
+)
+
+func TestNamespaceUser_Create(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ NamespaceID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ a web.Auth
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "NamespaceUsers Create normally",
+ fields: fields{
+ UserID: 1,
+ NamespaceID: 2,
+ },
+ },
+ {
+ name: "NamespaceUsers Create for duplicate",
+ fields: fields{
+ UserID: 1,
+ NamespaceID: 2,
+ },
+ wantErr: true,
+ errType: IsErrUserAlreadyHasNamespaceAccess,
+ },
+ {
+ name: "NamespaceUsers Create with invalid right",
+ fields: fields{
+ UserID: 1,
+ NamespaceID: 2,
+ Right: 500,
+ },
+ wantErr: true,
+ errType: IsErrInvalidUserRight,
+ },
+ {
+ name: "NamespaceUsers Create with inexisting list",
+ fields: fields{
+ UserID: 1,
+ NamespaceID: 2000,
+ },
+ wantErr: true,
+ errType: IsErrNamespaceDoesNotExist,
+ },
+ {
+ name: "NamespaceUsers Create with inexisting user",
+ fields: fields{
+ UserID: 500,
+ NamespaceID: 2,
+ },
+ wantErr: true,
+ errType: IsErrUserDoesNotExist,
+ },
+ {
+ name: "NamespaceUsers Create with the owner as shared user",
+ fields: fields{
+ UserID: 1,
+ NamespaceID: 1,
+ },
+ wantErr: true,
+ errType: IsErrUserAlreadyHasNamespaceAccess,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ un := &NamespaceUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ NamespaceID: tt.fields.NamespaceID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ err := un.Create(tt.args.a)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("NamespaceUser.Create() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("NamespaceUser.Create() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ })
+ }
+}
+
+func TestNamespaceUser_ReadAll(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ NamespaceID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ search string
+ a web.Auth
+ page int
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want interface{}
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "Test readall normal",
+ fields: fields{
+ NamespaceID: 3,
+ },
+ args: args{
+ a: &User{ID: 3},
+ },
+ want: []*UserWithRight{
+ {
+ User: User{
+ ID: 1,
+ Username: "user1",
+ Password: "1234",
+ Email: "johndoe@example.com",
+ },
+ Right: UserRightRead,
+ },
+ {
+ User: User{
+ ID: 2,
+ Username: "user2",
+ Password: "1234",
+ Email: "johndoe@example.com",
+ },
+ Right: UserRightRead,
+ },
+ },
+ },
+ {
+ name: "Test ReadAll by a user who does not have access to the list",
+ fields: fields{
+ NamespaceID: 3,
+ },
+ args: args{
+ a: &User{ID: 4},
+ },
+ wantErr: true,
+ errType: IsErrNeedToHaveNamespaceReadAccess,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ un := &NamespaceUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ NamespaceID: tt.fields.NamespaceID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ got, err := un.ReadAll(tt.args.search, tt.args.a, tt.args.page)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("NamespaceUser.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("NamespaceUser.ReadAll() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("NamespaceUser.ReadAll() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestNamespaceUser_Update(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ NamespaceID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ tests := []struct {
+ name string
+ fields fields
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "Test Update Normally",
+ fields: fields{
+ NamespaceID: 3,
+ UserID: 1,
+ Right: UserRightAdmin,
+ },
+ },
+ {
+ name: "Test Update to write",
+ fields: fields{
+ NamespaceID: 3,
+ UserID: 1,
+ Right: UserRightWrite,
+ },
+ },
+ {
+ name: "Test Update to Read",
+ fields: fields{
+ NamespaceID: 3,
+ UserID: 1,
+ Right: UserRightRead,
+ },
+ },
+ {
+ name: "Test Update with invalid right",
+ fields: fields{
+ NamespaceID: 3,
+ UserID: 1,
+ Right: 500,
+ },
+ wantErr: true,
+ errType: IsErrInvalidUserRight,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ nu := &NamespaceUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ NamespaceID: tt.fields.NamespaceID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ err := nu.Update()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("NamespaceUser.Update() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("NamespaceUser.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ })
+ }
+}
+
+func TestNamespaceUser_Delete(t *testing.T) {
+ type fields struct {
+ ID int64
+ UserID int64
+ NamespaceID int64
+ Right UserRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ tests := []struct {
+ name string
+ fields fields
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "Try deleting some unexistant user",
+ fields: fields{
+ UserID: 1000,
+ NamespaceID: 2,
+ },
+ wantErr: true,
+ errType: IsErrUserDoesNotExist,
+ },
+ {
+ name: "Try deleting a user which does not has access but exists",
+ fields: fields{
+ UserID: 1,
+ NamespaceID: 4,
+ },
+ wantErr: true,
+ errType: IsErrUserDoesNotHaveAccessToNamespace,
+ },
+ {
+ name: "Try deleting normally",
+ fields: fields{
+ UserID: 1,
+ NamespaceID: 3,
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ nu := &NamespaceUser{
+ ID: tt.fields.ID,
+ UserID: tt.fields.UserID,
+ NamespaceID: tt.fields.NamespaceID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ err := nu.Delete()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("NamespaceUser.Delete() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("NamespaceUser.Delete() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ })
+ }
+}
diff --git a/pkg/models/team_list_test.go b/pkg/models/team_list_test.go
index 524d7b748..83516707a 100644
--- a/pkg/models/team_list_test.go
+++ b/pkg/models/team_list_test.go
@@ -17,8 +17,10 @@
package models
import (
+ "code.vikunja.io/web"
"github.com/stretchr/testify/assert"
"reflect"
+ "runtime"
"testing"
)
@@ -85,7 +87,7 @@ func TestTeamList(t *testing.T) {
// Test read all for a list where the user not has access
tl6 := tl
- tl6.ListID = 3
+ tl6.ListID = 4
_, err = tl6.ReadAll("", &u, 1)
assert.Error(t, err)
assert.True(t, IsErrNeedToHaveListReadAccess(err))
@@ -104,5 +106,79 @@ func TestTeamList(t *testing.T) {
err = tl4.Delete()
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotHaveAccessToList(err))
-
+}
+
+func TestTeamList_Update(t *testing.T) {
+ type fields struct {
+ ID int64
+ TeamID int64
+ ListID int64
+ Right TeamRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ tests := []struct {
+ name string
+ fields fields
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "Test Update Normally",
+ fields: fields{
+ ListID: 3,
+ TeamID: 1,
+ Right: TeamRightAdmin,
+ },
+ },
+ {
+ name: "Test Update to write",
+ fields: fields{
+ ListID: 3,
+ TeamID: 1,
+ Right: TeamRightWrite,
+ },
+ },
+ {
+ name: "Test Update to Read",
+ fields: fields{
+ ListID: 3,
+ TeamID: 1,
+ Right: TeamRightRead,
+ },
+ },
+ {
+ name: "Test Update with invalid right",
+ fields: fields{
+ ListID: 3,
+ TeamID: 1,
+ Right: 500,
+ },
+ wantErr: true,
+ errType: IsErrInvalidTeamRight,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tl := &TeamList{
+ ID: tt.fields.ID,
+ TeamID: tt.fields.TeamID,
+ ListID: tt.fields.ListID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ err := tl.Update()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("TeamList.Update() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("TeamList.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ })
+ }
}
diff --git a/pkg/models/team_namespace_rights_test.go b/pkg/models/team_namespace_rights_test.go
new file mode 100644
index 000000000..19c70cb61
--- /dev/null
+++ b/pkg/models/team_namespace_rights_test.go
@@ -0,0 +1,99 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2018 Vikunja and contributors. All rights reserved.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+package models
+
+import (
+ "testing"
+
+ "code.vikunja.io/web"
+)
+
+func TestTeamNamespace_CanDoSomething(t *testing.T) {
+ type fields struct {
+ ID int64
+ TeamID int64
+ NamespaceID int64
+ Right TeamRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ a web.Auth
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want map[string]bool
+ }{
+ {
+ name: "CanDoSomething Normally",
+ fields: fields{
+ NamespaceID: 3,
+ },
+ args: args{
+ a: &User{ID: 3},
+ },
+ want: map[string]bool{"CanCreate": true, "CanDelete": true, "CanUpdate": true},
+ },
+ {
+ name: "CanDoSomething for a nonexistant namespace",
+ fields: fields{
+ NamespaceID: 300,
+ },
+ args: args{
+ a: &User{ID: 3},
+ },
+ want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
+ },
+ {
+ name: "CanDoSomething where the user does not have the rights",
+ fields: fields{
+ NamespaceID: 3,
+ },
+ args: args{
+ a: &User{ID: 4},
+ },
+ want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tn := &TeamNamespace{
+ ID: tt.fields.ID,
+ TeamID: tt.fields.TeamID,
+ NamespaceID: tt.fields.NamespaceID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ if got := tn.CanCreate(tt.args.a); got != tt.want["CanCreate"] {
+ t.Errorf("TeamNamespace.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
+ }
+ if got := tn.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
+ t.Errorf("TeamNamespace.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
+ }
+ if got := tn.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
+ t.Errorf("TeamNamespace.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
+ }
+ })
+ }
+}
diff --git a/pkg/models/team_namespace_test.go b/pkg/models/team_namespace_test.go
index 7575b4695..085f67b0a 100644
--- a/pkg/models/team_namespace_test.go
+++ b/pkg/models/team_namespace_test.go
@@ -17,8 +17,10 @@
package models
import (
+ "code.vikunja.io/web"
"github.com/stretchr/testify/assert"
"reflect"
+ "runtime"
"testing"
)
@@ -96,5 +98,79 @@ func TestTeamNamespace(t *testing.T) {
err = tn4.Delete()
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotHaveAccessToNamespace(err))
-
+}
+
+func TestTeamNamespace_Update(t *testing.T) {
+ type fields struct {
+ ID int64
+ TeamID int64
+ NamespaceID int64
+ Right TeamRight
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ tests := []struct {
+ name string
+ fields fields
+ wantErr bool
+ errType func(err error) bool
+ }{
+ {
+ name: "Test Update Normally",
+ fields: fields{
+ NamespaceID: 3,
+ TeamID: 1,
+ Right: TeamRightAdmin,
+ },
+ },
+ {
+ name: "Test Update to write",
+ fields: fields{
+ NamespaceID: 3,
+ TeamID: 1,
+ Right: TeamRightWrite,
+ },
+ },
+ {
+ name: "Test Update to Read",
+ fields: fields{
+ NamespaceID: 3,
+ TeamID: 1,
+ Right: TeamRightRead,
+ },
+ },
+ {
+ name: "Test Update with invalid right",
+ fields: fields{
+ NamespaceID: 3,
+ TeamID: 1,
+ Right: 500,
+ },
+ wantErr: true,
+ errType: IsErrInvalidTeamRight,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tl := &TeamNamespace{
+ ID: tt.fields.ID,
+ TeamID: tt.fields.TeamID,
+ NamespaceID: tt.fields.NamespaceID,
+ Right: tt.fields.Right,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+ err := tl.Update()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("TeamNamespace.Update() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if (err != nil) && tt.wantErr && !tt.errType(err) {
+ t.Errorf("TeamNamespace.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
+ }
+ })
+ }
}
diff --git a/pkg/models/teams_rights_test.go b/pkg/models/teams_rights_test.go
new file mode 100644
index 000000000..2b25d5aeb
--- /dev/null
+++ b/pkg/models/teams_rights_test.go
@@ -0,0 +1,110 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2018 Vikunja and contributors. All rights reserved.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+package models
+
+import (
+ "testing"
+
+ "code.vikunja.io/web"
+)
+
+func TestTeam_CanDoSomething(t *testing.T) {
+ type fields struct {
+ ID int64
+ Name string
+ Description string
+ CreatedByID int64
+ CreatedBy User
+ Members []*TeamUser
+ Created int64
+ Updated int64
+ CRUDable web.CRUDable
+ Rights web.Rights
+ }
+ type args struct {
+ a web.Auth
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want map[string]bool
+ }{
+ {
+ name: "CanDoSomething Normally",
+ fields: fields{
+ ID: 1,
+ },
+ args: args{
+ a: &User{ID: 1},
+ },
+ want: map[string]bool{"CanCreate": true, "IsAdmin": true, "CanRead": true, "CanDelete": true, "CanUpdate": true},
+ },
+ {
+ name: "CanDoSomething for a nonexistant namespace",
+ fields: fields{
+ ID: 300,
+ },
+ args: args{
+ a: &User{ID: 1},
+ },
+ want: map[string]bool{"CanCreate": true, "IsAdmin": false, "CanRead": false, "CanDelete": false, "CanUpdate": false},
+ },
+ {
+ name: "CanDoSomething where the user does not have the rights",
+ fields: fields{
+ ID: 1,
+ },
+ args: args{
+ a: &User{ID: 4},
+ },
+ want: map[string]bool{"CanCreate": true, "IsAdmin": false, "CanRead": false, "CanDelete": false, "CanUpdate": false},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tm := &Team{
+ ID: tt.fields.ID,
+ Name: tt.fields.Name,
+ Description: tt.fields.Description,
+ CreatedByID: tt.fields.CreatedByID,
+ CreatedBy: tt.fields.CreatedBy,
+ Members: tt.fields.Members,
+ Created: tt.fields.Created,
+ Updated: tt.fields.Updated,
+ CRUDable: tt.fields.CRUDable,
+ Rights: tt.fields.Rights,
+ }
+
+ if got := tm.CanCreate(tt.args.a); got != tt.want["CanCreate"] { // CanCreate is currently always true
+ t.Errorf("Team.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
+ }
+ if got := tm.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
+ t.Errorf("Team.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
+ }
+ if got := tm.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
+ t.Errorf("Team.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
+ }
+ if got := tm.CanRead(tt.args.a); got != tt.want["CanRead"] {
+ t.Errorf("Team.CanRead() = %v, want %v", got, tt.want["CanRead"])
+ }
+ if got := tm.IsAdmin(tt.args.a); got != tt.want["IsAdmin"] {
+ t.Errorf("Team.IsAdmin() = %v, want %v", got, tt.want["IsAdmin"])
+ }
+ })
+ }
+}
diff --git a/pkg/models/teams_test.go b/pkg/models/teams_test.go
index eae5929a1..801505f4b 100644
--- a/pkg/models/teams_test.go
+++ b/pkg/models/teams_test.go
@@ -47,6 +47,11 @@ func TestTeam_Create(t *testing.T) {
assert.True(t, tm.Members[0].Admin)
assert.True(t, dummyteam.CanRead(&doer))
+ // Try getting a team with an ID < 0
+ _, err = GetTeamByID(-1)
+ assert.Error(t, err)
+ assert.True(t, IsErrTeamDoesNotExist(err))
+
// Get all teams the user is part of
ts, err := tm.ReadAll("", &doer, 1)
assert.NoError(t, err)
diff --git a/pkg/models/user_email_confirm_test.go b/pkg/models/user_email_confirm_test.go
new file mode 100644
index 000000000..7957bbe29
--- /dev/null
+++ b/pkg/models/user_email_confirm_test.go
@@ -0,0 +1,67 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2018 Vikunja and contributors. All rights reserved.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+package models
+
+import "testing"
+
+func TestUserEmailConfirm(t *testing.T) {
+ type args struct {
+ c *EmailConfirm
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ errType func(error) bool
+ }{
+ {
+ name: "Test Empty token",
+ args: args{
+ c: &EmailConfirm{
+ Token: "",
+ },
+ },
+ wantErr: true,
+ errType: IsErrInvalidEmailConfirmToken,
+ },
+ {
+ name: "Test invalid token",
+ args: args{
+ c: &EmailConfirm{
+ Token: "invalid",
+ },
+ },
+ wantErr: true,
+ errType: IsErrInvalidEmailConfirmToken,
+ },
+ {
+ name: "Test valid token",
+ args: args{
+ c: &EmailConfirm{
+ Token: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := UserEmailConfirm(tt.args.c); (err != nil) != tt.wantErr {
+ t.Errorf("UserEmailConfirm() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
diff --git a/pkg/utils/random_string_test.go b/pkg/utils/random_string_test.go
new file mode 100644
index 000000000..2b8b2f884
--- /dev/null
+++ b/pkg/utils/random_string_test.go
@@ -0,0 +1,28 @@
+// Vikunja is a todo-list application to facilitate your life.
+// Copyright 2018 Vikunja and contributors. All rights reserved.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+package utils
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestMakeRandomString(t *testing.T) {
+ rand := MakeRandomString(32)
+ assert.NotEqual(t, rand, "loremipsuim")
+ assert.Equal(t, len(rand), 32)
+}
diff --git a/pkg/utils/sha256_test.go b/pkg/utils/sha256_test.go
new file mode 100644
index 000000000..3c780bb41
--- /dev/null
+++ b/pkg/utils/sha256_test.go
@@ -0,0 +1,55 @@
+/*
+ * Vikunja is a todo-list application to facilitate your life.
+ * Copyright 2018 Vikunja and contributors. All rights reserved.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+package utils
+
+import "testing"
+
+func TestSha256(t *testing.T) {
+ type args struct {
+ cleartext string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ name: "Test sha256 generation",
+ args: args{cleartext: "vikunjarandomstringwhichisnotrandom"},
+ want: "46fc0f603dd986cf7ed3e631917d43da89a8df2bdf291",
+ },
+ {
+ name: "Test sha256 generation",
+ args: args{cleartext: "vikunjastring"},
+ want: "f54d310f4d9a0bc13479dad5c5701e8d581744666b69f",
+ },
+ {
+ name: "Test sha256 generation",
+ args: args{cleartext: "somethingsomething"},
+ want: "00aef67d6df7fdee0419aa3713820e7084cbcb8b8f7c4",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := Sha256(tt.args.cleartext); got != tt.want {
+ t.Errorf("Sha256() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/tools.go b/tools.go
index 8e734ee32..b6fde2c39 100644
--- a/tools.go
+++ b/tools.go
@@ -22,6 +22,7 @@ package tools
import (
_ "github.com/client9/misspell/cmd/misspell"
+ _ "github.com/cweill/gotests/..."
_ "github.com/fzipp/gocyclo"
_ "github.com/gordonklaus/ineffassign"
_ "github.com/karalabe/xgo"