diff --git a/pkg/modules/migration/ticktick/ticktick.go b/pkg/modules/migration/ticktick/ticktick.go index d16307735..a7b3f84bc 100644 --- a/pkg/modules/migration/ticktick/ticktick.go +++ b/pkg/modules/migration/ticktick/ticktick.go @@ -146,12 +146,16 @@ func sortParentsBeforeChildren(tasks []*tickTickTask) []*tickTickTask { tasksByID[t.TaskID] = t } - placed := make(map[tickTickNumber]bool, len(tasks)) + // placed is keyed by the task itself rather than by TaskID: malformed + // exports can collapse several taskIds to 0 (see tickTickNumber), and + // keying by ID would treat every zero-ID task after the first as already + // placed and silently drop it. + placed := make(map[*tickTickTask]bool, len(tasks)) result := make([]*tickTickTask, 0, len(tasks)) var place func(t *tickTickTask) place = func(t *tickTickTask) { - if placed[t.TaskID] { + if placed[t] { return } // If this task has a parent that we know about, place the parent first. @@ -160,7 +164,7 @@ func sortParentsBeforeChildren(tasks []*tickTickTask) []*tickTickTask { place(parent) } } - placed[t.TaskID] = true + placed[t] = true result = append(result, t) } diff --git a/pkg/modules/migration/ticktick/ticktick_test.go b/pkg/modules/migration/ticktick/ticktick_test.go index 52d35fd24..64c60689d 100644 --- a/pkg/modules/migration/ticktick/ticktick_test.go +++ b/pkg/modules/migration/ticktick/ticktick_test.go @@ -846,3 +846,26 @@ func TestNonNumericNumberColumns(t *testing.T) { } assert.Equal(t, int64(1), priority) } + +// TestMultipleTasksWithMalformedIDsAreNotDropped guards against a regression +// where collapsing several unparseable taskIds to 0 caused all but the first +// zero-ID task to be silently dropped by sortParentsBeforeChildren. +func TestMultipleTasksWithMalformedIDsAreNotDropped(t *testing.T) { + tasks := []*tickTickTask{ + {TaskID: 0, ProjectName: "Project 1", Title: "First malformed"}, + {TaskID: 0, ProjectName: "Project 1", Title: "Second malformed"}, + {TaskID: 0, ProjectName: "Project 1", Title: "Third malformed"}, + } + + sorted := sortParentsBeforeChildren(tasks) + require.Len(t, sorted, 3, "no task with a zero ID should be dropped") + + vikunjaTasks := convertTickTickToVikunja(tasks) + titles := []string{} + for _, project := range vikunjaTasks { + for _, task := range project.Tasks { + titles = append(titles, task.Title) + } + } + assert.ElementsMatch(t, []string{"First malformed", "Second malformed", "Third malformed"}, titles) +}