// Vikunja is a to-do list application to facilitate your life. // Copyright 2018-2020 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" "time" "code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/db" "code.vikunja.io/api/pkg/files" "code.vikunja.io/api/pkg/user" "code.vikunja.io/web" "gopkg.in/d4l3k/messagediff.v1" ) func TestTaskCollection_ReadAll(t *testing.T) { // Dummy users user1 := &user.User{ ID: 1, Username: "user1", Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", IsActive: true, Issuer: "local", EmailRemindersEnabled: true, Created: testCreatedTime, Updated: testUpdatedTime, } user2 := &user.User{ ID: 2, Username: "user2", Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", Issuer: "local", EmailRemindersEnabled: true, Created: testCreatedTime, Updated: testUpdatedTime, } user6 := &user.User{ ID: 6, Username: "user6", Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", Issuer: "local", IsActive: true, EmailRemindersEnabled: true, Created: testCreatedTime, Updated: testUpdatedTime, } loc := config.GetTimeZone() label4 := &Label{ ID: 4, Title: "Label #4 - visible via other task", CreatedByID: 2, CreatedBy: user2, Created: testCreatedTime, Updated: testUpdatedTime, } // We use individual variables for the tasks here to be able to rearrange or remove ones more easily task1 := &Task{ ID: 1, Title: "task #1", Description: "Lorem Ipsum", Identifier: "test1-1", Index: 1, CreatedByID: 1, CreatedBy: user1, ListID: 1, BucketID: 1, IsFavorite: true, Labels: []*Label{ label4, }, RelatedTasks: map[RelationKind][]*Task{ RelationKindSubtask: { { ID: 29, Title: "task #29 with parent task (1)", Index: 14, CreatedByID: 1, ListID: 1, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), }, }, }, Attachments: []*TaskAttachment{ { ID: 1, TaskID: 1, FileID: 1, CreatedByID: 1, CreatedBy: user1, Created: testCreatedTime, File: &files.File{ ID: 1, Name: "test", Size: 100, Created: time.Unix(1570998791, 0).In(loc), CreatedByID: 1, }, }, { ID: 2, TaskID: 1, FileID: 9999, CreatedByID: 1, CreatedBy: user1, Created: testCreatedTime, }, }, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task2 := &Task{ ID: 2, Title: "task #2 done", Identifier: "test1-2", Index: 2, Done: true, CreatedByID: 1, CreatedBy: user1, ListID: 1, BucketID: 1, Labels: []*Label{ label4, }, RelatedTasks: map[RelationKind][]*Task{}, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task3 := &Task{ ID: 3, Title: "task #3 high prio", Identifier: "test1-3", Index: 3, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), Priority: 100, BucketID: 2, } task4 := &Task{ ID: 4, Title: "task #4 low prio", Identifier: "test1-4", Index: 4, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), Priority: 1, BucketID: 2, } task5 := &Task{ ID: 5, Title: "task #5 higher due date", Identifier: "test1-5", Index: 5, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), DueDate: time.Unix(1543636724, 0).In(loc), BucketID: 2, } task6 := &Task{ ID: 6, Title: "task #6 lower due date", Identifier: "test1-6", Index: 6, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), DueDate: time.Unix(1543616724, 0).In(loc), BucketID: 3, } task7 := &Task{ ID: 7, Title: "task #7 with start date", Identifier: "test1-7", Index: 7, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), StartDate: time.Unix(1544600000, 0).In(loc), BucketID: 3, } task8 := &Task{ ID: 8, Title: "task #8 with end date", Identifier: "test1-8", Index: 8, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), EndDate: time.Unix(1544700000, 0).In(loc), BucketID: 3, } task9 := &Task{ ID: 9, Title: "task #9 with start and end date", Identifier: "test1-9", Index: 9, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), StartDate: time.Unix(1544600000, 0).In(loc), EndDate: time.Unix(1544700000, 0).In(loc), } task10 := &Task{ ID: 10, Title: "task #10 basic", Identifier: "test1-10", Index: 10, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task11 := &Task{ ID: 11, Title: "task #11 basic", Identifier: "test1-11", Index: 11, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task12 := &Task{ ID: 12, Title: "task #12 basic", Identifier: "test1-12", Index: 12, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task15 := &Task{ ID: 15, Title: "task #15", Identifier: "test6-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 6, IsFavorite: true, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 6, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task16 := &Task{ ID: 16, Title: "task #16", Identifier: "test7-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 7, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 7, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task17 := &Task{ ID: 17, Title: "task #17", Identifier: "test8-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 8, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 8, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task18 := &Task{ ID: 18, Title: "task #18", Identifier: "test9-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 9, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 9, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task19 := &Task{ ID: 19, Title: "task #19", Identifier: "test10-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 10, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 10, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task20 := &Task{ ID: 20, Title: "task #20", Identifier: "test11-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 11, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 11, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task21 := &Task{ ID: 21, Title: "task #21", Identifier: "test12-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 12, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 12, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task22 := &Task{ ID: 22, Title: "task #22", Identifier: "test13-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 13, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 13, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task23 := &Task{ ID: 23, Title: "task #23", Identifier: "test14-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 14, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 14, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task24 := &Task{ ID: 24, Title: "task #24", Identifier: "test15-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 15, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 15, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task25 := &Task{ ID: 25, Title: "task #25", Identifier: "test16-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 16, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 16, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task26 := &Task{ ID: 26, Title: "task #26", Identifier: "test17-1", Index: 1, CreatedByID: 6, CreatedBy: user6, ListID: 17, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 17, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task27 := &Task{ ID: 27, Title: "task #27 with reminders", Identifier: "test1-12", Index: 12, CreatedByID: 1, CreatedBy: user1, Reminders: []time.Time{ time.Unix(1543626724, 0).In(loc), time.Unix(1543626824, 0).In(loc), }, ListID: 1, BucketID: 1, RelatedTasks: map[RelationKind][]*Task{}, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task28 := &Task{ ID: 28, Title: "task #28 with repeat after", Identifier: "test1-13", Index: 13, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, RepeatAfter: 3600, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task29 := &Task{ ID: 29, Title: "task #29 with parent task (1)", Identifier: "test1-14", Index: 14, CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{ RelationKindParenttask: { { ID: 1, Title: "task #1", Description: "Lorem Ipsum", Index: 1, CreatedByID: 1, ListID: 1, IsFavorite: true, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), BucketID: 1, }, }, }, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task30 := &Task{ ID: 30, Title: "task #30 with assignees", Identifier: "test1-15", Index: 15, CreatedByID: 1, CreatedBy: user1, ListID: 1, Assignees: []*user.User{ user1, user2, }, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task31 := &Task{ ID: 31, Title: "task #31 with color", Identifier: "test1-16", Index: 16, HexColor: "f0f0f0", CreatedByID: 1, CreatedBy: user1, ListID: 1, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task32 := &Task{ ID: 32, Title: "task #32", Identifier: "test3-1", Index: 1, CreatedByID: 1, CreatedBy: user1, ListID: 3, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 21, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } task33 := &Task{ ID: 33, Title: "task #33 with percent done", Identifier: "test1-17", Index: 17, CreatedByID: 1, CreatedBy: user1, ListID: 1, PercentDone: 0.5, RelatedTasks: map[RelationKind][]*Task{}, BucketID: 1, Created: time.Unix(1543626724, 0).In(loc), Updated: time.Unix(1543626724, 0).In(loc), } type fields struct { ListID int64 Lists []*List SortBy []string // Is a string, since this is the place where a query string comes from the user OrderBy []string FilterBy []string FilterValue []string FilterComparator []string FilterIncludeNulls bool CRUDable web.CRUDable Rights web.Rights } type args struct { search string a web.Auth page int } type testcase struct { name string fields fields args args want interface{} wantErr bool } defaultArgs := args{ search: "", a: &user.User{ID: 1}, page: 0, } tests := []testcase{ { name: "ReadAll Tasks normally", fields: fields{}, args: defaultArgs, want: []*Task{ task1, task2, task3, task4, task5, task6, task7, task8, task9, task10, task11, task12, task15, task16, task17, task18, task19, task20, task21, task22, task23, task24, task25, task26, task27, task28, task29, task30, task31, task32, task33, }, wantErr: false, }, { // For more sorting tests see task_collection_sort_test.go name: "ReadAll Tasks sorted by done asc and id desc", fields: fields{ SortBy: []string{"done", "id"}, OrderBy: []string{"asc", "desc"}, }, args: defaultArgs, want: []*Task{ task33, task32, task31, task30, task29, task28, task27, task26, task25, task24, task23, task22, task21, task20, task19, task18, task17, task16, task15, task12, task11, task10, task9, task8, task7, task6, task5, task4, task3, task1, task2, }, wantErr: false, }, { name: "ReadAll Tasks with range", fields: fields{ FilterBy: []string{"start_date", "end_date"}, FilterValue: []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00"}, FilterComparator: []string{"greater", "less"}, }, args: defaultArgs, want: []*Task{ task7, task8, task9, }, wantErr: false, }, { name: "ReadAll Tasks with different range", fields: fields{ FilterBy: []string{"start_date", "end_date"}, FilterValue: []string{"2018-12-13T11:20:00+00:00", "2018-12-16T22:40:00+00:00"}, FilterComparator: []string{"greater", "less"}, }, args: defaultArgs, want: []*Task{ task8, task9, }, wantErr: false, }, { name: "ReadAll Tasks with range with start date only", fields: fields{ FilterBy: []string{"start_date"}, FilterValue: []string{"2018-12-12T07:33:20+00:00"}, FilterComparator: []string{"greater"}, }, args: defaultArgs, want: []*Task{}, wantErr: false, }, { name: "ReadAll Tasks with range with start date only and greater equals", fields: fields{ FilterBy: []string{"start_date"}, FilterValue: []string{"2018-12-12T07:33:20+00:00"}, FilterComparator: []string{"greater_equals"}, }, args: defaultArgs, want: []*Task{ task7, task9, }, wantErr: false, }, { name: "undone tasks only", fields: fields{ FilterBy: []string{"done"}, FilterValue: []string{"false"}, FilterComparator: []string{"equals"}, }, args: defaultArgs, want: []*Task{ task1, // Task 2 is done task3, task4, task5, task6, task7, task8, task9, task10, task11, task12, task15, task16, task17, task18, task19, task20, task21, task22, task23, task24, task25, task26, task27, task28, task29, task30, task31, task32, task33, }, wantErr: false, }, { name: "done tasks only", fields: fields{ FilterBy: []string{"done"}, FilterValue: []string{"true"}, FilterComparator: []string{"equals"}, }, args: defaultArgs, want: []*Task{ task2, }, wantErr: false, }, { name: "done tasks only - not equals done", fields: fields{ FilterBy: []string{"done"}, FilterValue: []string{"false"}, FilterComparator: []string{"not_equals"}, }, args: defaultArgs, want: []*Task{ task2, }, wantErr: false, }, { name: "range with nulls", fields: fields{ FilterBy: []string{"start_date", "end_date"}, FilterValue: []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00"}, FilterComparator: []string{"greater", "less"}, FilterIncludeNulls: true, }, args: defaultArgs, want: []*Task{ task1, // has nil dates task2, // has nil dates task3, // has nil dates task4, // has nil dates task5, // has nil dates task6, // has nil dates task7, task8, task9, task10, // has nil dates task11, // has nil dates task12, // has nil dates task15, // has nil dates task16, // has nil dates task17, // has nil dates task18, // has nil dates task19, // has nil dates task20, // has nil dates task21, // has nil dates task22, // has nil dates task23, // has nil dates task24, // has nil dates task25, // has nil dates task26, // has nil dates task27, // has nil dates task28, // has nil dates task29, // has nil dates task30, // has nil dates task31, // has nil dates task32, // has nil dates task33, // has nil dates }, wantErr: false, }, { name: "favorited tasks", args: defaultArgs, fields: fields{ ListID: FavoritesPseudoList.ID, }, want: []*Task{ task1, task15, // Task 34 is also a favorite, but on a list user 1 has no access to. }, }, { name: "filtered with like", fields: fields{ FilterBy: []string{"title"}, FilterValue: []string{"with"}, FilterComparator: []string{"like"}, }, args: defaultArgs, want: []*Task{ task7, task8, task9, task27, task28, task29, task30, task31, task33, }, wantErr: false, }, { name: "filtered reminders", fields: fields{ FilterBy: []string{"reminders", "reminders"}, FilterValue: []string{"2018-10-01T00:00:00+00:00", "2018-12-10T00:00:00+00:00"}, FilterComparator: []string{"greater", "less"}, }, args: defaultArgs, want: []*Task{ task27, }, wantErr: false, }, { name: "filter in", fields: fields{ FilterBy: []string{"id"}, FilterValue: []string{"1,2,34"}, // Task 34 is forbidden for user 1 FilterComparator: []string{"in"}, }, args: defaultArgs, want: []*Task{ task1, task2, }, wantErr: false, }, { name: "filter assignees", fields: fields{ FilterBy: []string{"assignees"}, FilterValue: []string{"1"}, FilterComparator: []string{"equals"}, }, args: defaultArgs, want: []*Task{ task30, }, wantErr: false, }, { name: "filter assignees in", fields: fields{ FilterBy: []string{"assignees"}, FilterValue: []string{"1,2"}, FilterComparator: []string{"in"}, }, args: defaultArgs, want: []*Task{ task30, }, wantErr: false, }, { name: "filter labels", fields: fields{ FilterBy: []string{"labels"}, FilterValue: []string{"4"}, FilterComparator: []string{"equals"}, }, args: defaultArgs, want: []*Task{ task1, task2, }, wantErr: false, }, { name: "filter list", fields: fields{ FilterBy: []string{"list_id"}, FilterValue: []string{"6"}, FilterComparator: []string{"equals"}, }, args: defaultArgs, want: []*Task{ task15, }, wantErr: false, }, { name: "filter namespace", fields: fields{ FilterBy: []string{"namespace"}, FilterValue: []string{"7"}, FilterComparator: []string{"equals"}, }, args: defaultArgs, want: []*Task{ task21, }, wantErr: false, }, { name: "filter namespace in", fields: fields{ FilterBy: []string{"namespace"}, FilterValue: []string{"7,8"}, FilterComparator: []string{"in"}, }, args: defaultArgs, want: []*Task{ task21, task22, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { db.LoadAndAssertFixtures(t) lt := &TaskCollection{ ListID: tt.fields.ListID, SortBy: tt.fields.SortBy, OrderBy: tt.fields.OrderBy, FilterBy: tt.fields.FilterBy, FilterValue: tt.fields.FilterValue, FilterComparator: tt.fields.FilterComparator, FilterIncludeNulls: tt.fields.FilterIncludeNulls, CRUDable: tt.fields.CRUDable, Rights: tt.fields.Rights, } got, _, _, err := lt.ReadAll(tt.args.a, tt.args.search, tt.args.page, 50) if (err != nil) != tt.wantErr { t.Errorf("Test %s, Task.ReadAll() error = %v, wantErr %v", tt.name, err, tt.wantErr) return } if diff, equal := messagediff.PrettyDiff(got, tt.want); !equal { if len(got.([]*Task)) == 0 && len(tt.want.([]*Task)) == 0 { return } t.Errorf("Test %s, Task.ReadAll() = %v, want %v, \ndiff: %v", tt.name, got, tt.want, diff) } }) } }