diff --git a/pkg/db/fixtures/task_relations.yml b/pkg/db/fixtures/task_relations.yml index 750f2e217..4f852a7d2 100644 --- a/pkg/db/fixtures/task_relations.yml +++ b/pkg/db/fixtures/task_relations.yml @@ -34,3 +34,27 @@ relation_kind: 'related' created_by_id: 1 created: 2018-12-01 15:13:12 +- id: 7 + task_id: 41 + other_task_id: 43 + relation_kind: 'subtask' + created_by_id: 15 + created: 2018-12-01 15:13:12 +- id: 8 + task_id: 43 + other_task_id: 41 + relation_kind: 'parenttask' + created_by_id: 15 + created: 2018-12-01 15:13:12 +- id: 9 + task_id: 41 + other_task_id: 44 + relation_kind: 'subtask' + created_by_id: 15 + created: 2018-12-01 15:13:12 +- id: 10 + task_id: 44 + other_task_id: 41 + relation_kind: 'parenttask' + created_by_id: 15 + created: 2018-12-01 15:13:12 diff --git a/pkg/db/fixtures/tasks.yml b/pkg/db/fixtures/tasks.yml index 77e11ae4d..7dc61a498 100644 --- a/pkg/db/fixtures/tasks.yml +++ b/pkg/db/fixtures/tasks.yml @@ -374,5 +374,61 @@ due_date: 2023-03-01 15:00:00 created: 2018-12-01 01:12:04 updated: 2018-12-01 01:12:04 - bucket_id: 1 + bucket_id: 38 position: 39 +- id: 41 + uid: 'uid-caldav-test-parent-task' + title: 'Parent task for Caldav Test' + description: 'Description Caldav Test' + priority: 3 + done: false + created_by_id: 15 + project_id: 36 + index: 40 + due_date: 2023-03-01 15:00:00 + created: 2018-12-01 01:12:04 + updated: 2018-12-01 01:12:04 + bucket_id: 38 + position: 40 +- id: 42 + uid: 'uid-caldav-test-parent-task-2' + title: 'Parent task for Caldav Test 2' + description: 'Description Caldav Test 2' + priority: 3 + done: false + created_by_id: 15 + project_id: 36 + index: 41 + due_date: 2023-03-01 15:00:00 + created: 2018-12-01 01:12:04 + updated: 2018-12-01 01:12:04 + bucket_id: 38 + position: 41 +- id: 43 + uid: 'uid-caldav-test-child-task' + title: 'Child task for Caldav Test' + description: 'Description Caldav Test' + priority: 3 + done: false + created_by_id: 15 + project_id: 36 + index: 42 + due_date: 2023-03-01 15:00:00 + created: 2018-12-01 01:12:04 + updated: 2018-12-01 01:12:04 + bucket_id: 38 + position: 42 +- id: 44 + uid: 'uid-caldav-test-child-task-2' + title: 'Child task for Caldav Test 2' + description: 'Description Caldav Test' + priority: 3 + done: false + created_by_id: 15 + project_id: 36 + index: 43 + due_date: 2023-03-01 15:00:00 + created: 2018-12-01 01:12:04 + updated: 2018-12-01 01:12:04 + bucket_id: 38 + position: 43 diff --git a/pkg/routes/caldav/listStorageProvider_test.go b/pkg/routes/caldav/listStorageProvider_test.go new file mode 100644 index 000000000..a828a99af --- /dev/null +++ b/pkg/routes/caldav/listStorageProvider_test.go @@ -0,0 +1,443 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present 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 Affero General Public Licensee 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 Affero General Public Licensee for more details. +// +// You should have received a copy of the GNU Affero General Public Licensee +// along with this program. If not, see . + +package caldav + +// This file tests logic related to handling tasks in CALDAV format + +import ( + "code.vikunja.io/api/pkg/db" + "code.vikunja.io/api/pkg/models" + "code.vikunja.io/api/pkg/user" + "github.com/stretchr/testify/assert" + "testing" +) + +// Check logic related to creating sub-tasks +func TestSubTask_Create(t *testing.T) { + u := &user.User{ + ID: 15, + Username: "user15", + Email: "user15@example.com", + } + + // + // Create a subtask + // + t.Run("create", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + const taskUID = "uid_child1" + const taskContent = `BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +X-PUBLISHED-TTL:PT4H +X-WR-CALNAME:Project 36 for Caldav tests +PRODID:-//Vikunja Todo App//EN +BEGIN:VTODO +UID:uid_child1 +DTSTAMP:20230301T073337Z +SUMMARY:Caldav child task 1 +CREATED:20230301T073337Z +LAST-MODIFIED:20230301T073337Z +RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task +END:VTODO +END:VCALENDAR` + + storage := &VikunjaCaldavProjectStorage{ + project: &models.ProjectWithTasksAndBuckets{Project: models.Project{ID: 36}}, + task: &models.Task{UID: taskUID}, + user: u, + } + + // Create the subtask: + taskResource, err := storage.CreateResource(taskUID, taskContent) + assert.NoError(t, err) + + // Check that the result CALDAV contains the relation: + content, _ := taskResource.GetContentData() + assert.Contains(t, content, "UID:"+taskUID) + assert.Contains(t, content, "RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task") + + // Get the task from the DB: + tasks, err := models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task := tasks[0] + + // Check that the parent-child relationship is present: + assert.Len(t, task.RelatedTasks[models.RelationKindParenttask], 1) + parentTask := task.RelatedTasks[models.RelationKindParenttask][0] + assert.Equal(t, "uid-caldav-test-parent-task", parentTask.UID) + }) + + // + // Create a subtask on a subtask, i.e. create a grand-child + // + t.Run("create grandchild on child task", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + const taskUID = "uid_child1" + const taskContent = `BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +X-PUBLISHED-TTL:PT4H +X-WR-CALNAME:Project 36 for Caldav tests +PRODID:-//Vikunja Todo App//EN +BEGIN:VTODO +UID:uid_child1 +DTSTAMP:20230301T073337Z +SUMMARY:Caldav child task 1 +CREATED:20230301T073337Z +LAST-MODIFIED:20230301T073337Z +RELATED-TO;RELTYPE=PARENT:uid-caldav-test-child-task +END:VTODO +END:VCALENDAR` + + storage := &VikunjaCaldavProjectStorage{ + project: &models.ProjectWithTasksAndBuckets{Project: models.Project{ID: 36}}, + task: &models.Task{UID: taskUID}, + user: u, + } + + // Create the task: + taskResource, err := storage.CreateResource(taskUID, taskContent) + assert.NoError(t, err) + + // Check that the result CALDAV contains the relation: + content, _ := taskResource.GetContentData() + assert.Contains(t, content, "UID:"+taskUID) + assert.Contains(t, content, "RELATED-TO;RELTYPE=PARENT:uid-caldav-test-child-task") + + // Get the task from the DB: + tasks, err := models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task := tasks[0] + + // Check that the parent-child relationship of the grandchildren is present: + assert.Len(t, task.RelatedTasks[models.RelationKindParenttask], 1) + parentTask := task.RelatedTasks[models.RelationKindParenttask][0] + assert.Equal(t, "uid-caldav-test-child-task", parentTask.UID) + + // Get the child task and check that it now has a parent and a child: + tasks, err = models.GetTasksByUIDs(s, []string{"uid-caldav-test-child-task"}, u) + assert.NoError(t, err) + task = tasks[0] + assert.Len(t, task.RelatedTasks[models.RelationKindParenttask], 1) + parentTask = task.RelatedTasks[models.RelationKindParenttask][0] + assert.Equal(t, "uid-caldav-test-parent-task", parentTask.UID) + assert.Len(t, task.RelatedTasks[models.RelationKindSubtask], 1) + childTask := task.RelatedTasks[models.RelationKindSubtask][0] + assert.Equal(t, taskUID, childTask.UID) + }) + + // + // Create a subtask on a parent that we don't know anything about (yet) + // + t.Run("create subtask on unknown parent", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + // Create a subtask: + const taskUID = "uid_child1" + const taskContent = `BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +X-PUBLISHED-TTL:PT4H +X-WR-CALNAME:Project 36 for Caldav tests +PRODID:-//Vikunja Todo App//EN +BEGIN:VTODO +UID:uid_child1 +DTSTAMP:20230301T073337Z +SUMMARY:Caldav child task 1 +CREATED:20230301T073337Z +LAST-MODIFIED:20230301T073337Z +RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-doesnt-exist-yet +END:VTODO +END:VCALENDAR` + + storage := &VikunjaCaldavProjectStorage{ + project: &models.ProjectWithTasksAndBuckets{Project: models.Project{ID: 36}}, + task: &models.Task{UID: taskUID}, + user: u, + } + + // Create the task: + taskResource, err := storage.CreateResource(taskUID, taskContent) + assert.NoError(t, err) + + // Check that the result CALDAV contains the relation: + content, _ := taskResource.GetContentData() + assert.Contains(t, content, "UID:"+taskUID) + assert.Contains(t, content, "RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-doesnt-exist-yet") + + // Get the task from the DB: + tasks, err := models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task := tasks[0] + + // Check that the parent-child relationship is present: + assert.Len(t, task.RelatedTasks[models.RelationKindParenttask], 1) + parentTask := task.RelatedTasks[models.RelationKindParenttask][0] + assert.Equal(t, "uid-caldav-test-parent-doesnt-exist-yet", parentTask.UID) + + // Check that the non-existent parent task was created in the process: + tasks, err = models.GetTasksByUIDs(s, []string{"uid-caldav-test-parent-doesnt-exist-yet"}, u) + assert.NoError(t, err) + task = tasks[0] + assert.Equal(t, "uid-caldav-test-parent-doesnt-exist-yet", task.UID) + }) +} + +// Logic related to editing tasks and subtasks +func TestSubTask_Edit(t *testing.T) { + u := &user.User{ + ID: 15, + Username: "user15", + Email: "user15@example.com", + } + + // + // Edit a subtask and check that the relations are not gone + // + t.Run("edit subtask", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + // Edit the subtask: + const taskUID = "uid-caldav-test-child-task" + const taskContent = `BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +X-PUBLISHED-TTL:PT4H +X-WR-CALNAME:Project 36 for Caldav tests +PRODID:-//Vikunja Todo App//EN +BEGIN:VTODO +UID:uid-caldav-test-child-task +DTSTAMP:20230301T073337Z +SUMMARY:Child task for Caldav Test (edited) +CREATED:20230301T073337Z +LAST-MODIFIED:20230301T073337Z +RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task +END:VTODO +END:VCALENDAR` + tasks, err := models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task := tasks[0] + storage := &VikunjaCaldavProjectStorage{ + project: &models.ProjectWithTasksAndBuckets{Project: models.Project{ID: 36}}, + task: task, + user: u, + } + + // Edit the task: + taskResource, err := storage.UpdateResource(taskUID, taskContent) + assert.NoError(t, err) + + // Check that the result CALDAV still contains the relation: + content, _ := taskResource.GetContentData() + assert.Contains(t, content, "UID:"+taskUID) + assert.Contains(t, content, "RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task") + + // Get the task from the DB: + tasks, err = models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task = tasks[0] + + // Check that the parent-child relationship is still present: + assert.Len(t, task.RelatedTasks[models.RelationKindParenttask], 1) + parentTask := task.RelatedTasks[models.RelationKindParenttask][0] + assert.Equal(t, "uid-caldav-test-parent-task", parentTask.UID) + }) + + // + // Edit a parent task and check that the subtasks are still linked + // + t.Run("edit parent", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + // Edit the parent task: + const taskUID = "uid-caldav-test-parent-task" + const taskContent = `BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +X-PUBLISHED-TTL:PT4H +X-WR-CALNAME:Project 36 for Caldav tests +PRODID:-//Vikunja Todo App//EN +BEGIN:VTODO +UID:uid-caldav-test-parent-task +DTSTAMP:20230301T073337Z +SUMMARY:Parent task for Caldav Test (edited) +CREATED:20230301T073337Z +LAST-MODIFIED:20230301T073337Z +END:VTODO +END:VCALENDAR` + tasks, err := models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task := tasks[0] + storage := &VikunjaCaldavProjectStorage{ + project: &models.ProjectWithTasksAndBuckets{Project: models.Project{ID: 36}}, + task: task, + user: u, + } + + // Edit the task: + _, err = storage.UpdateResource(taskUID, taskContent) + assert.NoError(t, err) + + // Get the task from the DB: + tasks, err = models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task = tasks[0] + + // Check that the subtasks are still linked: + assert.Len(t, task.RelatedTasks[models.RelationKindSubtask], 2) + existingSubTask := task.RelatedTasks[models.RelationKindSubtask][0] + assert.Equal(t, "uid-caldav-test-child-task", existingSubTask.UID) + existingSubTask = task.RelatedTasks[models.RelationKindSubtask][1] + assert.Equal(t, "uid-caldav-test-child-task-2", existingSubTask.UID) + }) + + // + // Edit a subtask and change its parent + // + t.Run("edit subtask change parent", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + // Edit the subtask: + const taskUID = "uid-caldav-test-child-task" + const taskContent = `BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +X-PUBLISHED-TTL:PT4H +X-WR-CALNAME:Project 36 for Caldav tests +PRODID:-//Vikunja Todo App//EN +BEGIN:VTODO +UID:uid-caldav-test-child-task +DTSTAMP:20230301T073337Z +SUMMARY:Child task for Caldav Test (edited) +CREATED:20230301T073337Z +LAST-MODIFIED:20230301T073337Z +RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task-2 +END:VTODO +END:VCALENDAR` + tasks, err := models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task := tasks[0] + storage := &VikunjaCaldavProjectStorage{ + project: &models.ProjectWithTasksAndBuckets{Project: models.Project{ID: 36}}, + task: task, + user: u, + } + + // Edit the task: + taskResource, err := storage.UpdateResource(taskUID, taskContent) + assert.NoError(t, err) + + // Check that the result CALDAV contains the new relation: + content, _ := taskResource.GetContentData() + assert.Contains(t, content, "UID:"+taskUID) + assert.Contains(t, content, "RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task-2") + + // Get the task from the DB: + tasks, err = models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task = tasks[0] + + // Check that the parent-child relationship has changed to the new parent: + assert.Len(t, task.RelatedTasks[models.RelationKindParenttask], 1) + parentTask := task.RelatedTasks[models.RelationKindParenttask][0] + assert.Equal(t, "uid-caldav-test-parent-task-2", parentTask.UID) + + // Get the previous parent from the DB and check that its previous child is gone: + tasks, err = models.GetTasksByUIDs(s, []string{"uid-caldav-test-parent-task"}, u) + assert.NoError(t, err) + task = tasks[0] + assert.Len(t, task.RelatedTasks[models.RelationKindSubtask], 1) + // We're gone, but our former sibling is still there: + formerSiblingSubTask := task.RelatedTasks[models.RelationKindSubtask][0] + assert.Equal(t, "uid-caldav-test-child-task-2", formerSiblingSubTask.UID) + }) + + // + // Edit a subtask and remove its parent + // + t.Run("edit subtask remove parent", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + // Edit the subtask: + const taskUID = "uid-caldav-test-child-task" + const taskContent = `BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +X-PUBLISHED-TTL:PT4H +X-WR-CALNAME:Project 36 for Caldav tests +PRODID:-//Vikunja Todo App//EN +BEGIN:VTODO +UID:uid-caldav-test-child-task +DTSTAMP:20230301T073337Z +SUMMARY:Child task for Caldav Test (edited) +CREATED:20230301T073337Z +LAST-MODIFIED:20230301T073337Z +END:VTODO +END:VCALENDAR` + tasks, err := models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task := tasks[0] + storage := &VikunjaCaldavProjectStorage{ + project: &models.ProjectWithTasksAndBuckets{Project: models.Project{ID: 36}}, + task: task, + user: u, + } + + // Edit the task: + taskResource, err := storage.UpdateResource(taskUID, taskContent) + assert.NoError(t, err) + + // Check that the result CALDAV contains the new relation: + content, _ := taskResource.GetContentData() + assert.Contains(t, content, "UID:"+taskUID) + assert.NotContains(t, content, "RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task") + + // Get the task from the DB: + tasks, err = models.GetTasksByUIDs(s, []string{taskUID}, u) + assert.NoError(t, err) + task = tasks[0] + + // Check that the parent-child relationship is gone: + assert.Len(t, task.RelatedTasks[models.RelationKindParenttask], 0) + + // Get the previous parent from the DB and check that its child is gone: + tasks, err = models.GetTasksByUIDs(s, []string{"uid-caldav-test-parent-task"}, u) + assert.NoError(t, err) + task = tasks[0] + // We're gone, but our former sibling is still there: + assert.Len(t, task.RelatedTasks[models.RelationKindSubtask], 1) + formerSiblingSubTask := task.RelatedTasks[models.RelationKindSubtask][0] + assert.Equal(t, "uid-caldav-test-child-task-2", formerSiblingSubTask.UID) + }) +} diff --git a/pkg/routes/caldav/main_test.go b/pkg/routes/caldav/main_test.go new file mode 100644 index 000000000..7093df18e --- /dev/null +++ b/pkg/routes/caldav/main_test.go @@ -0,0 +1,19 @@ +package caldav + +import ( + "code.vikunja.io/api/pkg/config" + "code.vikunja.io/api/pkg/files" + "code.vikunja.io/api/pkg/models" + "code.vikunja.io/api/pkg/user" + "testing" +) + +// TestMain is the main test function used to bootstrap the test env +func TestMain(m *testing.M) { + config.InitDefaultConfig() + files.InitTests(true) + user.InitTests() + models.SetupTests() + + m.Run() +}