Task Position (#412)
Fix misspell Fix sorting tasks with null values Fix sorting by priority for postgres Merge branch 'master' into feature/position Add community link Update golang.org/x/crypto commit hash to 44a6062 (#429) Update golang.org/x/crypto commit hash to 44a6062 Reviewed-on: https://kolaente.dev/vikunja/api/pulls/429 Update module lib/pq to v1.4.0 (#428) Update module lib/pq to v1.4.0 Reviewed-on: https://kolaente.dev/vikunja/api/pulls/428 Fix updating position Add ordering tasks in buckets by position Make task sort by string Merge branch 'master' into feature/position Update golang.org/x/crypto commit hash to 3c4aac8 (#419) Update golang.org/x/crypto commit hash to 3c4aac8 Reviewed-on: https://kolaente.dev/vikunja/api/pulls/419 Merge branch 'master' into feature/position Fix moving tasks back into the empty (ID: 0) bucket Add adding a default position when creating new tasks Update golang.org/x/crypto commit hash to a76a400 (#411) Update golang.org/x/crypto commit hash to a76a400 Reviewed-on: https://kolaente.dev/vikunja/api/pulls/411 Remove unused code Fix tests Add migration for position attribute Add position attribute Co-authored-by: kolaente <k@knt.li> Co-authored-by: renovate <renovatebot@kolaente.de> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/412
This commit is contained in:
@ -17,10 +17,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/mohae/deepcopy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -62,11 +59,12 @@ func TestSortParamValidation(t *testing.T) {
|
||||
taskPropertyUID,
|
||||
taskPropertyCreated,
|
||||
taskPropertyUpdated,
|
||||
taskPropertyPosition,
|
||||
} {
|
||||
t.Run(test, func(t *testing.T) {
|
||||
s := &sortParam{
|
||||
orderBy: orderAscending,
|
||||
sortBy: sortProperty(test),
|
||||
sortBy: test,
|
||||
}
|
||||
err := s.validate()
|
||||
assert.NoError(t, err)
|
||||
@ -92,774 +90,3 @@ func TestSortParamValidation(t *testing.T) {
|
||||
assert.True(t, IsErrInvalidTaskField(err))
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
task1 = &Task{
|
||||
ID: 1,
|
||||
Text: "aaa",
|
||||
Description: "Lorem Ipsum",
|
||||
Done: true,
|
||||
DoneAt: 1543626000,
|
||||
ListID: 1,
|
||||
UID: "JywtBPCESImlyKugvaZWrxmXAFAWXFISMeXYImEh",
|
||||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
}
|
||||
task2 = &Task{
|
||||
ID: 2,
|
||||
Text: "bbb",
|
||||
Description: "Arem Ipsum",
|
||||
Done: true,
|
||||
DoneAt: 1543626724,
|
||||
CreatedByID: 1,
|
||||
ListID: 2,
|
||||
PercentDone: 0.3,
|
||||
StartDate: 1543626724,
|
||||
Created: 1553626724,
|
||||
Updated: 1553626724,
|
||||
}
|
||||
task3 = &Task{
|
||||
ID: 3,
|
||||
Text: "ccc",
|
||||
DueDate: 1583626724,
|
||||
Priority: 100,
|
||||
ListID: 3,
|
||||
HexColor: "000000",
|
||||
PercentDone: 0.1,
|
||||
Updated: 1555555555,
|
||||
}
|
||||
task4 = &Task{
|
||||
ID: 4,
|
||||
Text: "ddd",
|
||||
Priority: 1,
|
||||
StartDate: 1643626724,
|
||||
ListID: 1,
|
||||
}
|
||||
task5 = &Task{
|
||||
ID: 5,
|
||||
Text: "eef",
|
||||
Priority: 50,
|
||||
UID: "shggzCHQWLhGNMNsOGOCOjcVkInOYjTAnORqTkdL",
|
||||
DueDate: 1543636724,
|
||||
Updated: 1565555555,
|
||||
}
|
||||
task6 = &Task{
|
||||
ID: 6,
|
||||
Text: "eef",
|
||||
DueDate: 1543616724,
|
||||
RepeatAfter: 6400,
|
||||
CreatedByID: 2,
|
||||
HexColor: "ffffff",
|
||||
}
|
||||
task7 = &Task{
|
||||
ID: 7,
|
||||
Text: "mmmn",
|
||||
Description: "Zoremis",
|
||||
StartDate: 1544600000,
|
||||
EndDate: 1584600000,
|
||||
UID: "tyzCZuLMSKhwclJOsDyDcUdyVAPBDOPHNTBOLTcW",
|
||||
}
|
||||
task8 = &Task{
|
||||
ID: 8,
|
||||
Text: "b123",
|
||||
EndDate: 1544700000,
|
||||
}
|
||||
task9 = &Task{
|
||||
ID: 9,
|
||||
Done: true,
|
||||
DoneAt: 1573626724,
|
||||
Text: "a123",
|
||||
RepeatAfter: 86000,
|
||||
StartDate: 1544600000,
|
||||
EndDate: 1544700000,
|
||||
}
|
||||
task10 = &Task{
|
||||
ID: 10,
|
||||
Text: "zzz",
|
||||
Priority: 10,
|
||||
PercentDone: 1,
|
||||
}
|
||||
)
|
||||
|
||||
type taskSortTestCase struct {
|
||||
name string
|
||||
wantAsc []*Task
|
||||
wantDesc []*Task
|
||||
sortProperty string
|
||||
}
|
||||
|
||||
var taskSortTestCases = []taskSortTestCase{
|
||||
{
|
||||
name: "id",
|
||||
sortProperty: taskPropertyID,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task10,
|
||||
task9,
|
||||
task8,
|
||||
task7,
|
||||
task6,
|
||||
task5,
|
||||
task4,
|
||||
task3,
|
||||
task2,
|
||||
task1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "text",
|
||||
sortProperty: taskPropertyText,
|
||||
wantAsc: []*Task{
|
||||
task9,
|
||||
task1,
|
||||
task8,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task10,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task10,
|
||||
task7,
|
||||
task5,
|
||||
task6,
|
||||
task4,
|
||||
task3,
|
||||
task2,
|
||||
task8,
|
||||
task1,
|
||||
task9,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
sortProperty: taskPropertyDescription,
|
||||
wantAsc: []*Task{
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task2,
|
||||
task1,
|
||||
task7,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task7,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "done",
|
||||
sortProperty: taskPropertyDone,
|
||||
wantAsc: []*Task{
|
||||
// These are not
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
// These are done
|
||||
task1,
|
||||
task2,
|
||||
task9,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
// These are done
|
||||
task1,
|
||||
task2,
|
||||
task9,
|
||||
// These are not
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "done at",
|
||||
sortProperty: taskPropertyDoneAtUnix,
|
||||
wantAsc: []*Task{
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
task1,
|
||||
task2,
|
||||
task9,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task9,
|
||||
task2,
|
||||
task1,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "due date",
|
||||
sortProperty: taskPropertyDueDateUnix,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task4,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task6,
|
||||
task5,
|
||||
task3,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task3,
|
||||
task5,
|
||||
task6,
|
||||
task1,
|
||||
task2,
|
||||
task4,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "created by id",
|
||||
sortProperty: taskPropertyCreatedByID,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task2,
|
||||
task6,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task6,
|
||||
task2,
|
||||
task1,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list id",
|
||||
sortProperty: taskPropertyListID,
|
||||
wantAsc: []*Task{
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task1,
|
||||
task4,
|
||||
task2,
|
||||
task3,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task3,
|
||||
task2,
|
||||
task1,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "repeat after",
|
||||
sortProperty: taskPropertyRepeatAfter,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
task6,
|
||||
task9,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task9,
|
||||
task6,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "priority",
|
||||
sortProperty: taskPropertyPriority,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task4,
|
||||
task10,
|
||||
task5,
|
||||
task3,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task3,
|
||||
task5,
|
||||
task10,
|
||||
task4,
|
||||
task1,
|
||||
task2,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "start date",
|
||||
sortProperty: taskPropertyStartDateUnix,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task3,
|
||||
task5,
|
||||
task6,
|
||||
task8,
|
||||
task10,
|
||||
task2,
|
||||
task7,
|
||||
task9,
|
||||
task4,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task4,
|
||||
task7,
|
||||
task9,
|
||||
task2,
|
||||
task1,
|
||||
task3,
|
||||
task5,
|
||||
task6,
|
||||
task8,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "end date",
|
||||
sortProperty: taskPropertyEndDateUnix,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task10,
|
||||
task8,
|
||||
task9,
|
||||
task7,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hex color",
|
||||
sortProperty: taskPropertyHexColor,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task3,
|
||||
task6,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task6,
|
||||
task3,
|
||||
task1,
|
||||
task2,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "percent done",
|
||||
sortProperty: taskPropertyPercentDone,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task3,
|
||||
task2,
|
||||
task10,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task10,
|
||||
task2,
|
||||
task3,
|
||||
task1,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "uid",
|
||||
sortProperty: taskPropertyUID,
|
||||
wantAsc: []*Task{
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task6,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task1,
|
||||
task5,
|
||||
task7,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task7,
|
||||
task5,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task6,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "created",
|
||||
sortProperty: taskPropertyCreated,
|
||||
wantAsc: []*Task{
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task1,
|
||||
task2,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task2,
|
||||
task1,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updated",
|
||||
sortProperty: taskPropertyUpdated,
|
||||
wantAsc: []*Task{
|
||||
task4,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task5,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task5,
|
||||
task3,
|
||||
task2,
|
||||
task1,
|
||||
task4,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestTaskSort(t *testing.T) {
|
||||
|
||||
assertTestSliceMatch := func(t *testing.T, got, want []*Task) {
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("Slices do not match in order")
|
||||
t.Error("Got\t| Want")
|
||||
for in, task := range got {
|
||||
fail := ""
|
||||
if task.ID != want[in].ID {
|
||||
fail = "wrong"
|
||||
}
|
||||
t.Errorf("\t%d\t| %d \t%s", task.ID, want[in].ID, fail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, testCase := range taskSortTestCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Run("asc default", func(t *testing.T) {
|
||||
by := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(testCase.sortProperty),
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(testCase.wantAsc).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, by)
|
||||
|
||||
assertTestSliceMatch(t, got, testCase.wantAsc)
|
||||
})
|
||||
t.Run("asc", func(t *testing.T) {
|
||||
by := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(testCase.sortProperty),
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(testCase.wantAsc).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, by)
|
||||
|
||||
assertTestSliceMatch(t, got, testCase.wantAsc)
|
||||
})
|
||||
t.Run("desc", func(t *testing.T) {
|
||||
by := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(testCase.sortProperty),
|
||||
orderBy: orderDescending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(testCase.wantDesc).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, by)
|
||||
|
||||
assertTestSliceMatch(t, got, testCase.wantDesc)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Other cases
|
||||
t.Run("Order by Done Ascending and ID Descending", func(t *testing.T) {
|
||||
want := []*Task{
|
||||
// Not done
|
||||
task10,
|
||||
task8,
|
||||
task7,
|
||||
task6,
|
||||
task5,
|
||||
task4,
|
||||
task3,
|
||||
|
||||
// Done
|
||||
task9,
|
||||
task2,
|
||||
task1,
|
||||
}
|
||||
sortParams := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyDone),
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyID),
|
||||
orderBy: orderDescending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(want).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, sortParams)
|
||||
|
||||
assertTestSliceMatch(t, got, want)
|
||||
})
|
||||
t.Run("Order by Done Ascending and Text Descending", func(t *testing.T) {
|
||||
want := []*Task{
|
||||
// Not done
|
||||
task10,
|
||||
task7,
|
||||
task5,
|
||||
task6,
|
||||
task4,
|
||||
task3,
|
||||
task8,
|
||||
// Done
|
||||
task2,
|
||||
task1,
|
||||
task9,
|
||||
}
|
||||
sortParams := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyDone),
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyText),
|
||||
orderBy: orderDescending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(want).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, sortParams)
|
||||
|
||||
assertTestSliceMatch(t, got, want)
|
||||
})
|
||||
t.Run("Order by Done Descending and Text Ascending", func(t *testing.T) {
|
||||
want := []*Task{
|
||||
// Done
|
||||
task9,
|
||||
task1,
|
||||
task2,
|
||||
// Not done
|
||||
task8,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task10,
|
||||
}
|
||||
sortParams := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyDone),
|
||||
orderBy: orderDescending,
|
||||
},
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyText),
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(want).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, sortParams)
|
||||
|
||||
assertTestSliceMatch(t, got, want)
|
||||
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user