mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-04-25 06:35:32 +00:00
fix(events): only trigger task.updated once when marking task done
Resolves https://github.com/go-vikunja/vikunja/issues/1724
This commit is contained in:
@@ -61,3 +61,20 @@ func TestListener(t *testing.T, event Event, listener Listener) {
|
|||||||
err = listener.Handle(msg)
|
err = listener.Handle(msg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearDispatchedEvents clears the list of dispatched test events. This is useful when you want to
|
||||||
|
// test event dispatch counts in a specific section of code without events from previous test operations.
|
||||||
|
func ClearDispatchedEvents() {
|
||||||
|
dispatchedTestEvents = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountDispatchedEvents counts how many events of a specific type have been dispatched.
|
||||||
|
func CountDispatchedEvents(eventName string) int {
|
||||||
|
count := 0
|
||||||
|
for _, testEvent := range dispatchedTestEvents {
|
||||||
|
if testEvent.Name() == eventName {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|||||||
@@ -83,23 +83,8 @@ func (b *TaskBucket) upsert(s *xorm.Session) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update is the handler to update a task bucket
|
// updateTaskBucket is internally used to actually do the update.
|
||||||
// @Summary Update a task bucket
|
func updateTaskBucket(s *xorm.Session, a web.Auth, b *TaskBucket) (err error) {
|
||||||
// @Description Updates a task in a bucket
|
|
||||||
// @tags task
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Security JWTKeyAuth
|
|
||||||
// @Param project path int true "Project ID"
|
|
||||||
// @Param view path int true "Project View ID"
|
|
||||||
// @Param bucket path int true "Bucket ID"
|
|
||||||
// @Param taskBucket body models.TaskBucket true "The id of the task you want to move into the bucket."
|
|
||||||
// @Success 200 {object} models.TaskBucket "The updated task bucket."
|
|
||||||
// @Failure 400 {object} web.HTTPError "Invalid task bucket object provided."
|
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
|
||||||
// @Router /projects/{project}/views/{view}/buckets/{bucket}/tasks [post]
|
|
||||||
func (b *TaskBucket) Update(s *xorm.Session, a web.Auth) (err error) {
|
|
||||||
|
|
||||||
oldTaskBucket := &TaskBucket{}
|
oldTaskBucket := &TaskBucket{}
|
||||||
_, err = s.
|
_, err = s.
|
||||||
Where("task_id = ? AND project_view_id = ?", b.TaskID, b.ProjectViewID).
|
Where("task_id = ? AND project_view_id = ?", b.TaskID, b.ProjectViewID).
|
||||||
@@ -192,7 +177,7 @@ func (b *TaskBucket) Update(s *xorm.Session, a web.Auth) (err error) {
|
|||||||
|
|
||||||
err = task.updateReminders(s, task)
|
err = task.updateReminders(s, task)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the done state of the task was changed, we need to move the task into all done buckets everywhere
|
// Since the done state of the task was changed, we need to move the task into all done buckets everywhere
|
||||||
@@ -230,9 +215,33 @@ func (b *TaskBucket) Update(s *xorm.Session, a web.Auth) (err error) {
|
|||||||
b.Task = task
|
b.Task = task
|
||||||
b.Bucket = bucket
|
b.Bucket = bucket
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is the handler to update a task bucket
|
||||||
|
// @Summary Update a task bucket
|
||||||
|
// @Description Updates a task in a bucket
|
||||||
|
// @tags task
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param project path int true "Project ID"
|
||||||
|
// @Param view path int true "Project View ID"
|
||||||
|
// @Param bucket path int true "Bucket ID"
|
||||||
|
// @Param taskBucket body models.TaskBucket true "The id of the task you want to move into the bucket."
|
||||||
|
// @Success 200 {object} models.TaskBucket "The updated task bucket."
|
||||||
|
// @Failure 400 {object} web.HTTPError "Invalid task bucket object provided."
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /projects/{project}/views/{view}/buckets/{bucket}/tasks [post]
|
||||||
|
func (b *TaskBucket) Update(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
err = updateTaskBucket(s, a, b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
doer, _ := user.GetFromAuth(a)
|
doer, _ := user.GetFromAuth(a)
|
||||||
return events.Dispatch(&TaskUpdatedEvent{
|
return events.Dispatch(&TaskUpdatedEvent{
|
||||||
Task: task,
|
Task: b.Task,
|
||||||
Doer: doer,
|
Doer: doer,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,21 +54,9 @@ func (tp *TaskPosition) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
|||||||
return t.CanUpdate(s, a)
|
return t.CanUpdate(s, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update is the handler to update a task position
|
// updateTaskPosition is the internal function that performs the task position update logic
|
||||||
// @Summary Updates a task position
|
// without dispatching events. This is used by moveTaskToDoneBuckets to avoid duplicate events.
|
||||||
// @Description Updates a task position.
|
func updateTaskPosition(s *xorm.Session, a web.Auth, tp *TaskPosition) (err error) {
|
||||||
// @tags task
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Security JWTKeyAuth
|
|
||||||
// @Param id path int true "Task ID"
|
|
||||||
// @Param view body models.TaskPosition true "The task position with updated values you want to change."
|
|
||||||
// @Success 200 {object} models.TaskPosition "The updated task position."
|
|
||||||
// @Failure 400 {object} web.HTTPError "Invalid task position object provided."
|
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
|
||||||
// @Router /tasks/{id}/position [post]
|
|
||||||
func (tp *TaskPosition) Update(s *xorm.Session, a web.Auth) (err error) {
|
|
||||||
|
|
||||||
// Update all positions if the newly saved position is < 0.1
|
// Update all positions if the newly saved position is < 0.1
|
||||||
var shouldRecalculate bool
|
var shouldRecalculate bool
|
||||||
var view *ProjectView
|
var view *ProjectView
|
||||||
@@ -110,6 +98,28 @@ func (tp *TaskPosition) Update(s *xorm.Session, a web.Auth) (err error) {
|
|||||||
return RecalculateTaskPositions(s, view, a)
|
return RecalculateTaskPositions(s, view, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is the handler to update a task position
|
||||||
|
// @Summary Updates a task position
|
||||||
|
// @Description Updates a task position.
|
||||||
|
// @tags task
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param id path int true "Task ID"
|
||||||
|
// @Param view body models.TaskPosition true "The task position with updated values you want to change."
|
||||||
|
// @Success 200 {object} models.TaskPosition "The updated task position."
|
||||||
|
// @Failure 400 {object} web.HTTPError "Invalid task position object provided."
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /tasks/{id}/position [post]
|
||||||
|
func (tp *TaskPosition) Update(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
err = updateTaskPosition(s, a, tp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return triggerTaskUpdatedEventForTaskID(s, a, tp.TaskID)
|
return triggerTaskUpdatedEventForTaskID(s, a, tp.TaskID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1173,7 +1173,7 @@ func (t *Task) updateSingleTask(s *xorm.Session, a web.Auth, fields []string) (e
|
|||||||
ProjectViewID: view.ID,
|
ProjectViewID: view.ID,
|
||||||
ProjectID: t.ProjectID,
|
ProjectID: t.ProjectID,
|
||||||
}
|
}
|
||||||
err = tb.Update(s, a)
|
err = updateTaskBucket(s, a, tb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1183,7 +1183,7 @@ func (t *Task) updateSingleTask(s *xorm.Session, a web.Auth, fields []string) (e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tp.Update(s, a)
|
err = updateTaskPosition(s, a, tp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1404,7 +1404,7 @@ func (t *Task) moveTaskToDoneBuckets(s *xorm.Session, a web.Auth, views []*Proje
|
|||||||
ProjectViewID: view.ID,
|
ProjectViewID: view.ID,
|
||||||
ProjectID: t.ProjectID,
|
ProjectID: t.ProjectID,
|
||||||
}
|
}
|
||||||
err = tb.Update(s, a)
|
err = updateTaskBucket(s, a, tb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1414,7 +1414,7 @@ func (t *Task) moveTaskToDoneBuckets(s *xorm.Session, a web.Auth, views []*Proje
|
|||||||
ProjectViewID: view.ID,
|
ProjectViewID: view.ID,
|
||||||
Position: calculateDefaultPosition(t.Index, t.Position),
|
Position: calculateDefaultPosition(t.Index, t.Position),
|
||||||
}
|
}
|
||||||
err = tp.Update(s, a)
|
err = updateTaskPosition(s, a, &tp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,6 +261,27 @@ func TestTask_Update(t *testing.T) {
|
|||||||
"bucket_id": 3,
|
"bucket_id": 3,
|
||||||
}, false)
|
}, false)
|
||||||
})
|
})
|
||||||
|
t.Run("marking a task as done should fire exactly ONE task.updated event", func(t *testing.T) {
|
||||||
|
db.LoadAndAssertFixtures(t)
|
||||||
|
s := db.NewSession()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
// Clear any events from previous operations
|
||||||
|
events.ClearDispatchedEvents()
|
||||||
|
|
||||||
|
task := &Task{
|
||||||
|
ID: 1,
|
||||||
|
Done: true,
|
||||||
|
}
|
||||||
|
err := task.Update(s, u)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = s.Commit()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify exactly ONE task.updated event was dispatched
|
||||||
|
count := events.CountDispatchedEvents("task.updated")
|
||||||
|
assert.Equal(t, 1, count, "Expected exactly 1 task.updated event, got %d", count)
|
||||||
|
})
|
||||||
t.Run("move task to another project should use the default bucket", func(t *testing.T) {
|
t.Run("move task to another project should use the default bucket", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
|
|||||||
Reference in New Issue
Block a user