diff options
Diffstat (limited to 'internal/store/native_tasks_test.go')
| -rw-r--r-- | internal/store/native_tasks_test.go | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/internal/store/native_tasks_test.go b/internal/store/native_tasks_test.go index a299ac6..c362eb7 100644 --- a/internal/store/native_tasks_test.go +++ b/internal/store/native_tasks_test.go @@ -302,6 +302,37 @@ func TestCompleteNativeTask_AlreadySuperseded_DoesNotDoubleCreate(t *testing.T) } } +func TestCreateNextIteration_ConcurrentGuard_SecondCallIsNoOp(t *testing.T) { + s := newNativeTasksTestStore(t) + if _, err := s.db.Exec(` + INSERT INTO native_tasks (id, content, due_date, recurrence_freq, recurrence_interval, recurrence_series_id) + VALUES ('rec-1', 'Water plants', '2026-07-13', 'weekly', 1, 'series-1') + `); err != nil { + t.Fatal(err) + } + old, err := s.GetNativeTaskByID("rec-1") + if err != nil { + t.Fatal(err) + } + + // Simulate two concurrent triggers both computing from the same + // snapshot of rec-1 and both attempting to create its successor. + if err := s.CreateNextIteration(*old); err != nil { + t.Fatalf("first CreateNextIteration: %v", err) + } + if err := s.CreateNextIteration(*old); err != nil { + t.Fatalf("second CreateNextIteration (should be a safe no-op, not an error): %v", err) + } + + var count int + if err := s.db.QueryRow(`SELECT COUNT(*) FROM native_tasks WHERE recurrence_series_id = 'series-1' AND id != 'rec-1'`).Scan(&count); err != nil { + t.Fatal(err) + } + if count != 1 { + t.Errorf("expected exactly 1 successor row after two CreateNextIteration calls from the same snapshot, got %d", count) + } +} + func TestSetTaskRecurrence_FirstTimeGeneratesSeriesID(t *testing.T) { s := newNativeTasksTestStore(t) |
